fixup! block attributes
This commit is contained in:
		
					parent
					
						
							
								82e1fd74f5
							
						
					
				
			
			
				commit
				
					
						4cb9c07cfc
					
				
			
		
					 1 changed files with 31 additions and 17 deletions
				
			
		
							
								
								
									
										48
									
								
								src/attr.rs
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								src/attr.rs
									
										
									
									
									
								
							| 
						 | 
					@ -24,28 +24,30 @@ pub fn valid<I: Iterator<Item = char>>(chars: I) -> (usize, bool) {
 | 
				
			||||||
    (p.pos, has_attr)
 | 
					    (p.pos, has_attr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// A collection of attributes, i.e. a key-value map.
 | 
				
			||||||
// Attributes are relatively rare, we choose to pay 8 bytes always and sometimes an extra
 | 
					// Attributes are relatively rare, we choose to pay 8 bytes always and sometimes an extra
 | 
				
			||||||
// indirection instead of always 24 bytes.
 | 
					// indirection instead of always 24 bytes.
 | 
				
			||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
 | 
					#[derive(Debug, Clone, PartialEq, Eq, Default)]
 | 
				
			||||||
pub struct Attributes<'s>(Option<Box<Vec<(&'s str, CowStr<'s>)>>>);
 | 
					pub struct Attributes<'s>(Option<Box<Vec<(&'s str, CowStr<'s>)>>>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'s> Attributes<'s> {
 | 
					impl<'s> Attributes<'s> {
 | 
				
			||||||
 | 
					    /// Create an empty collection.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub fn new() -> Self {
 | 
				
			||||||
        Self::default()
 | 
					        Self::default()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn take(&mut self) -> Self {
 | 
					    pub(crate) fn take(&mut self) -> Self {
 | 
				
			||||||
        Self(self.0.take())
 | 
					        Self(self.0.take())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn parse<S: DiscontinuousString<'s>>(&mut self, input: S) -> bool {
 | 
					    pub(crate) fn parse<S: DiscontinuousString<'s>>(&mut self, input: S) -> bool {
 | 
				
			||||||
        for elem in Parser::new(input.chars()) {
 | 
					        for elem in Parser::new(input.chars()) {
 | 
				
			||||||
            match elem {
 | 
					            match elem {
 | 
				
			||||||
                Element::Class(c) => self.add("class", input.src(c)),
 | 
					                Element::Class(c) => self.insert("class", input.src(c)),
 | 
				
			||||||
                Element::Identifier(i) => self.add("id", input.src(i)),
 | 
					                Element::Identifier(i) => self.insert("id", input.src(i)),
 | 
				
			||||||
                Element::Attribute(a, v) => self.add(
 | 
					                Element::Attribute(a, v) => self.insert(
 | 
				
			||||||
                    match input.src(a) {
 | 
					                    match input.src(a) {
 | 
				
			||||||
                        CowStr::Owned(_) => panic!(),
 | 
					                        CowStr::Owned(_) => panic!(),
 | 
				
			||||||
                        CowStr::Borrowed(s) => s,
 | 
					                        CowStr::Borrowed(s) => s,
 | 
				
			||||||
| 
						 | 
					@ -59,7 +61,7 @@ impl<'s> Attributes<'s> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Combine all attributes from both objects, prioritizing self on conflicts.
 | 
					    /// Combine all attributes from both objects, prioritizing self on conflicts.
 | 
				
			||||||
    pub fn union(&mut self, other: Self) {
 | 
					    pub(crate) fn union(&mut self, other: Self) {
 | 
				
			||||||
        if let Some(attrs0) = &mut self.0 {
 | 
					        if let Some(attrs0) = &mut self.0 {
 | 
				
			||||||
            if let Some(mut attrs1) = other.0 {
 | 
					            if let Some(mut attrs1) = other.0 {
 | 
				
			||||||
                for (key, val) in attrs1.drain(..) {
 | 
					                for (key, val) in attrs1.drain(..) {
 | 
				
			||||||
| 
						 | 
					@ -73,38 +75,40 @@ impl<'s> Attributes<'s> {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn add(&mut self, attr: &'s str, val: CowStr<'s>) {
 | 
					    /// Insert an attribute. If the attribute already exists, the previous value will be
 | 
				
			||||||
 | 
					    /// overwritten, unless it is a "class" attribute. In that case the provided value will be
 | 
				
			||||||
 | 
					    /// appended to the existing value.
 | 
				
			||||||
 | 
					    pub fn insert(&mut self, key: &'s str, val: CowStr<'s>) {
 | 
				
			||||||
        if self.0.is_none() {
 | 
					        if self.0.is_none() {
 | 
				
			||||||
            self.0 = Some(Vec::new().into());
 | 
					            self.0 = Some(Vec::new().into());
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let attrs = self.0.as_mut().unwrap();
 | 
					        let attrs = self.0.as_mut().unwrap();
 | 
				
			||||||
        if let Some(i) = attrs.iter().position(|(a, _)| *a == attr) {
 | 
					        if let Some(i) = attrs.iter().position(|(k, _)| *k == key) {
 | 
				
			||||||
            let prev = &mut attrs[i].1;
 | 
					            let prev = &mut attrs[i].1;
 | 
				
			||||||
            if attr == "class" {
 | 
					            if key == "class" {
 | 
				
			||||||
                *prev = format!("{} {}", prev, val).into();
 | 
					                *prev = format!("{} {}", prev, val).into();
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                *prev = val;
 | 
					                *prev = val;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            attrs.push((attr, val));
 | 
					            attrs.push((key, val));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns true if the collection contains no attributes.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn is_empty(&self) -> bool {
 | 
					    pub fn is_empty(&self) -> bool {
 | 
				
			||||||
        if let Some(v) = &self.0 {
 | 
					        self.0.as_ref().map_or(true, |v| v.is_empty())
 | 
				
			||||||
            v.is_empty()
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns a reference to the value corresponding to the attribute key.
 | 
				
			||||||
    #[must_use]
 | 
					    #[must_use]
 | 
				
			||||||
    pub fn get(&self, key: &str) -> Option<&str> {
 | 
					    pub fn get(&self, key: &str) -> Option<&str> {
 | 
				
			||||||
        self.iter().find(|(k, _)| *k == key).map(|(_, v)| v)
 | 
					        self.iter().find(|(k, _)| *k == key).map(|(_, v)| v)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Returns an iterator over the attributes in undefined order.
 | 
				
			||||||
    pub fn iter(&self) -> impl Iterator<Item = (&'s str, &str)> + '_ {
 | 
					    pub fn iter(&self) -> impl Iterator<Item = (&'s str, &str)> + '_ {
 | 
				
			||||||
        self.0
 | 
					        self.0
 | 
				
			||||||
            .iter()
 | 
					            .iter()
 | 
				
			||||||
| 
						 | 
					@ -147,6 +151,7 @@ enum State {
 | 
				
			||||||
struct Parser<I> {
 | 
					struct Parser<I> {
 | 
				
			||||||
    chars: I,
 | 
					    chars: I,
 | 
				
			||||||
    pos: usize,
 | 
					    pos: usize,
 | 
				
			||||||
 | 
					    pos_prev: usize,
 | 
				
			||||||
    state: State,
 | 
					    state: State,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -155,12 +160,14 @@ impl<I: Iterator<Item = char>> Parser<I> {
 | 
				
			||||||
        Parser {
 | 
					        Parser {
 | 
				
			||||||
            chars,
 | 
					            chars,
 | 
				
			||||||
            pos: 0,
 | 
					            pos: 0,
 | 
				
			||||||
 | 
					            pos_prev: 0,
 | 
				
			||||||
            state: Start,
 | 
					            state: Start,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn step_char(&mut self) -> Option<State> {
 | 
					    fn step_char(&mut self) -> Option<State> {
 | 
				
			||||||
        self.chars.next().map(|c| {
 | 
					        self.chars.next().map(|c| {
 | 
				
			||||||
 | 
					            self.pos_prev = self.pos;
 | 
				
			||||||
            self.pos += c.len_utf8();
 | 
					            self.pos += c.len_utf8();
 | 
				
			||||||
            match self.state {
 | 
					            match self.state {
 | 
				
			||||||
                Start => match c {
 | 
					                Start => match c {
 | 
				
			||||||
| 
						 | 
					@ -236,7 +243,7 @@ impl<I: Iterator<Item = char>> Parser<I> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn step(&mut self) -> (State, Span) {
 | 
					    fn step(&mut self) -> (State, Span) {
 | 
				
			||||||
        let start = self.pos.saturating_sub(1);
 | 
					        let start = self.pos_prev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.state == Done {
 | 
					        if self.state == Done {
 | 
				
			||||||
            return (Done, Span::empty_at(start));
 | 
					            return (Done, Span::empty_at(start));
 | 
				
			||||||
| 
						 | 
					@ -250,14 +257,14 @@ impl<I: Iterator<Item = char>> Parser<I> {
 | 
				
			||||||
            if self.state != state_next {
 | 
					            if self.state != state_next {
 | 
				
			||||||
                return (
 | 
					                return (
 | 
				
			||||||
                    std::mem::replace(&mut self.state, state_next),
 | 
					                    std::mem::replace(&mut self.state, state_next),
 | 
				
			||||||
                    Span::new(start, self.pos - 1),
 | 
					                    Span::new(start, self.pos_prev),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
            if self.state == Done { Done } else { Invalid },
 | 
					            if self.state == Done { Done } else { Invalid },
 | 
				
			||||||
            Span::new(start, self.pos.saturating_sub(1)),
 | 
					            Span::new(start, self.pos_prev),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -342,6 +349,13 @@ mod test {
 | 
				
			||||||
            ("class", "some_class"),
 | 
					            ("class", "some_class"),
 | 
				
			||||||
            ("id", "some_id"),
 | 
					            ("id", "some_id"),
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					        test_attr!("{.a .b}", ("class", "a b"));
 | 
				
			||||||
 | 
					        test_attr!("{#a #b}", ("id", "b"));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn unicode_whitespace() {
 | 
				
			||||||
 | 
					        test_attr!("{.a .b}", ("class", "a b"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[test]
 | 
					    #[test]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue