diff --git a/src/lexer.rs b/src/lexer.rs index ae3622b..2367d16 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -4,7 +4,7 @@ use std::str::Chars; use crate::atrule::AtRuleKind; use crate::common::{Keyword, Op, Pos, Symbol}; -use crate::selector::{Attribute, AttributeKind}; +use crate::selector::{Attribute, AttributeKind, CaseKind}; use crate::{Token, TokenKind, Whitespace}; #[derive(Debug, Clone)] @@ -243,7 +243,7 @@ impl<'a> Lexer<'a> { kind: AttributeKind::Any, attr, value: String::new(), - case_sensitive: true, + case_sensitive: CaseKind::Sensitive, }) } 'i' => { @@ -253,7 +253,17 @@ impl<'a> Lexer<'a> { kind: AttributeKind::Any, attr, value: String::new(), - case_sensitive: false, + case_sensitive: CaseKind::InsensitiveLowercase, + }); + } + 'I' => { + self.devour_whitespace(); + assert!(self.buf.next() == Some(']')); + return TokenKind::Attribute(Attribute { + kind: AttributeKind::Any, + attr, + value: String::new(), + case_sensitive: CaseKind::InsensitiveCapital, }); } '=' => AttributeKind::Equals, @@ -272,14 +282,19 @@ impl<'a> Lexer<'a> { self.devour_whitespace(); let mut value = String::with_capacity(99); - let mut case_sensitive = true; + let mut case_sensitive = CaseKind::Sensitive; while let Some(c) = self.buf.peek() { if !c.is_alphabetic() && c != &'-' && c != &'_' && c != &'"' && c != &'\'' { break; } - if c == &'i' { + if c == &'i' || c == &'I' { + if c == &'i' { + case_sensitive = CaseKind::InsensitiveLowercase; + } else if c == &'I' { + case_sensitive = CaseKind::InsensitiveCapital; + } let tok = self .buf .next() @@ -287,7 +302,14 @@ impl<'a> Lexer<'a> { self.pos.next_char(); self.devour_whitespace(); match self.buf.next() { - Some(']') => case_sensitive = false, + Some(']') => { + return TokenKind::Attribute(Attribute { + kind, + attr, + value, + case_sensitive, + }) + } Some(val) => { self.pos.next_char(); value.push(tok); @@ -304,6 +326,7 @@ impl<'a> Lexer<'a> { .expect("this is impossible because we have already peeked"); self.pos.next_char(); value.push(tok); + self.devour_whitespace(); } self.devour_whitespace(); diff --git a/src/selector.rs b/src/selector.rs index 8993b34..778582b 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -329,38 +329,51 @@ impl Selector { } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct Attribute { +pub(crate) struct Attribute { pub attr: String, pub value: String, - pub case_sensitive: bool, + pub case_sensitive: CaseKind, pub kind: AttributeKind, } +#[derive(Clone, Debug, Eq, PartialEq)] +pub(crate) enum CaseKind { + InsensitiveCapital, + InsensitiveLowercase, + Sensitive, +} + +impl Display for CaseKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsensitiveCapital => write!(f, " I"), + Self::InsensitiveLowercase => write!(f, " i"), + Self::Sensitive => write!(f, ""), + } + } +} + impl Display for Attribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.case_sensitive { - match self.kind { - AttributeKind::Any => write!(f, "[{}]", self.attr), - AttributeKind::Equals => write!(f, "[{}={}]", self.attr, self.value), - AttributeKind::InList => write!(f, "[{}~={}]", self.attr, self.value), - AttributeKind::BeginsWithHyphenOrExact => { - write!(f, "[{}|={}]", self.attr, self.value) - } - AttributeKind::StartsWith => write!(f, "[{}^={}]", self.attr, self.value), - AttributeKind::EndsWith => write!(f, "[{}$={}]", self.attr, self.value), - AttributeKind::Contains => write!(f, "[{}*={}]", self.attr, self.value), + match self.kind { + AttributeKind::Any => write!(f, "[{}{}]", self.attr, self.case_sensitive), + AttributeKind::Equals => { + write!(f, "[{}={}{}]", self.attr, self.value, self.case_sensitive) } - } else { - match self.kind { - AttributeKind::Any => write!(f, "[{} i]", self.attr), - AttributeKind::Equals => write!(f, "[{}={} i]", self.attr, self.value), - AttributeKind::InList => write!(f, "[{}~={} i]", self.attr, self.value), - AttributeKind::BeginsWithHyphenOrExact => { - write!(f, "[{}|={} i]", self.attr, self.value) - } - AttributeKind::StartsWith => write!(f, "[{}^={} i]", self.attr, self.value), - AttributeKind::EndsWith => write!(f, "[{}$={} i]", self.attr, self.value), - AttributeKind::Contains => write!(f, "[{}*={} i]", self.attr, self.value), + AttributeKind::InList => { + write!(f, "[{}~={}{}]", self.attr, self.value, self.case_sensitive) + } + AttributeKind::BeginsWithHyphenOrExact => { + write!(f, "[{}|={}{}]", self.attr, self.value, self.case_sensitive) + } + AttributeKind::StartsWith => { + write!(f, "[{}^={}{}]", self.attr, self.value, self.case_sensitive) + } + AttributeKind::EndsWith => { + write!(f, "[{}$={}{}]", self.attr, self.value, self.case_sensitive) + } + AttributeKind::Contains => { + write!(f, "[{}*={}{}]", self.attr, self.value, self.case_sensitive) } } } diff --git a/tests/main.rs b/tests/main.rs index 066a8f2..664e84c 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -127,6 +127,14 @@ mod test_selectors { ); test!(selector_attribute_any, "[attr] {\n color: red;\n}\n"); + test!( + selector_attribute_any_lower_case_insensitive, + "[attr i] {\n color: red;\n}\n" + ); + test!( + selector_attribute_any_upper_case_insensitive, + "[attr I] {\n color: red;\n}\n" + ); test!( selector_attribute_equals, "[attr=val] {\n color: red;\n}\n"