diff --git a/float-pigment-css/src/group.rs b/float-pigment-css/src/group.rs index 64286a8..4dc1b4d 100644 --- a/float-pigment-css/src/group.rs +++ b/float-pigment-css/src/group.rs @@ -790,21 +790,21 @@ impl StyleSheetGroup { /// Query a single node selector (usually for testing only). /// /// Note that the font size and `em` values will be converted to `px` values. - pub fn query_single( + pub fn query_single( &self, - query: &StyleQuery, + query: T, media_query_status: &MediaQueryStatus, node_properties: &mut NodeProperties, ) { - self.query_ancestor_path(&[query.clone()], media_query_status, node_properties, None) + self.query_ancestor_path(&[query], media_query_status, node_properties, None) } /// Find rules that matches the query. /// /// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself. - pub fn for_each_matched_rule( + pub fn for_each_matched_rule( &self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, mut f: impl FnMut(MatchedRuleRef, Option<&LinkedStyleSheet>), ) { @@ -824,9 +824,9 @@ impl StyleSheetGroup { /// Get a rule list that matches the query. /// /// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself. - pub fn query_matched_rules( + pub fn query_matched_rules( &self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, ) -> MatchedRuleList { let mut rules = vec![]; @@ -845,9 +845,9 @@ impl StyleSheetGroup { /// /// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself. /// Note that the font size and `em` values will be converted to `px` values. - pub fn query_ancestor_path( + pub fn query_ancestor_path( &self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, node_properties: &mut NodeProperties, parent_node_properties: Option<&NodeProperties>, diff --git a/float-pigment-css/src/parser/mod.rs b/float-pigment-css/src/parser/mod.rs index 26e1276..497b30f 100644 --- a/float-pigment-css/src/parser/mod.rs +++ b/float-pigment-css/src/parser/mod.rs @@ -1611,7 +1611,7 @@ fn parse_attribute_selector<'a, 't: 'a, 'i: 't>( // [name=value] Ok(&Token::Delim('=')) => AttributeOperator::Exact, // [name~=value] - Ok(&Token::IncludeMatch) => AttributeOperator::Contain, + Ok(&Token::IncludeMatch) => AttributeOperator::List, // [name|=value] Ok(&Token::DashMatch) => AttributeOperator::Hyphen, // [name^=value] @@ -1619,7 +1619,7 @@ fn parse_attribute_selector<'a, 't: 'a, 'i: 't>( // [name$=value] Ok(&Token::SuffixMatch) => AttributeOperator::End, // [name*=value] - Ok(&Token::SubstringMatch) => AttributeOperator::List, + Ok(&Token::SubstringMatch) => AttributeOperator::Contain, Ok(_) => { return Err(location.new_custom_error(CustomError::UnexpectedTokenInAttributeSelector)) } diff --git a/float-pigment-css/src/query.rs b/float-pigment-css/src/query.rs index fe45e5f..94a1dbc 100644 --- a/float-pigment-css/src/query.rs +++ b/float-pigment-css/src/query.rs @@ -85,8 +85,45 @@ impl MediaQueryStatus { } } +pub trait StyleNodeClass { + fn name(&self) -> &str; + fn scope(&self) -> Option; +} + +impl StyleNodeClass for (String, Option) { + fn name(&self) -> &str { + &self.0 + } + + fn scope(&self) -> Option { + self.1 + } +} + +pub trait StyleNode { + type Class: StyleNodeClass; + type ClassIter<'a>: Iterator + where + Self: 'a; + + fn style_scope(&self) -> Option; + fn extra_style_scope(&self) -> Option; + fn host_style_scope(&self) -> Option; + fn tag_name(&self) -> &str; + fn id(&self) -> Option<&str>; + fn classes(&self) -> Self::ClassIter<'_>; + fn attribute(&self, name: &str) -> Option<&str>; + + fn contain_scope(&self, scope: Option) -> bool { + scope.is_none() + || self.style_scope() == scope + || self.extra_style_scope() == scope + || self.host_style_scope() == scope + } +} + /// Represents node information, used for matching rules. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct StyleQuery<'a> { pub(super) style_scope: Option, pub(super) extra_style_scope: Option, @@ -94,8 +131,19 @@ pub struct StyleQuery<'a> { pub(super) tag_name: &'a str, pub(super) id: &'a str, pub(super) classes: &'a [(String, Option)], - #[allow(unused)] - pub(super) attributes: &'a [String], // TODO support attributes +} + +impl Clone for StyleQuery<'_> { + fn clone(&self) -> Self { + Self { + style_scope: self.style_scope, + extra_style_scope: self.extra_style_scope, + host_style_scope: self.host_style_scope, + tag_name: self.tag_name, + id: self.id, + classes: self.classes, + } + } } impl<'a> StyleQuery<'a> { @@ -107,7 +155,6 @@ impl<'a> StyleQuery<'a> { tag_name: &'a str, id: &'a str, classes: &'a [(String, Option)], - attributes: &'a [String], ) -> Self { Self { style_scope, @@ -116,15 +163,79 @@ impl<'a> StyleQuery<'a> { tag_name, id, classes, - attributes, } } +} + +impl<'a> StyleNode for StyleQuery<'a> { + type Class = (String, Option); + type ClassIter<'c> + = core::slice::Iter<'c, Self::Class> + where + 'a: 'c; + + fn style_scope(&self) -> Option { + self.style_scope + } - pub(crate) fn contain_scope(&self, scope: Option) -> bool { - scope.is_none() - || self.style_scope == scope - || self.extra_style_scope == scope - || self.host_style_scope == scope + fn extra_style_scope(&self) -> Option { + self.extra_style_scope + } + + fn host_style_scope(&self) -> Option { + self.host_style_scope + } + + fn tag_name(&self) -> &str { + self.tag_name + } + + fn id(&self) -> Option<&str> { + Some(self.id) + } + + fn classes(&self) -> Self::ClassIter<'_> { + self.classes.iter() + } + + fn attribute(&self, name: &str) -> Option<&str> { + None + } +} + +impl<'b, 'a: 'b> StyleNode for &'b StyleQuery<'a> { + type Class = (String, Option); + type ClassIter<'c> + = core::slice::Iter<'c, Self::Class> + where + 'b: 'c; + + fn style_scope(&self) -> Option { + self.style_scope + } + + fn extra_style_scope(&self) -> Option { + self.extra_style_scope + } + + fn host_style_scope(&self) -> Option { + self.host_style_scope + } + + fn tag_name(&self) -> &str { + self.tag_name + } + + fn id(&self) -> Option<&str> { + Some(self.id) + } + + fn classes(&self) -> Self::ClassIter<'_> { + self.classes.iter() + } + + fn attribute(&self, name: &str) -> Option<&str> { + None } } diff --git a/float-pigment-css/src/sheet/mod.rs b/float-pigment-css/src/sheet/mod.rs index d28846a..c354b50 100644 --- a/float-pigment-css/src/sheet/mod.rs +++ b/float-pigment-css/src/sheet/mod.rs @@ -365,9 +365,9 @@ impl LinkedStyleSheet { Err(rule) } - pub(crate) fn for_each_matched_rule( + pub(crate) fn for_each_matched_rule( &self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, sheet_index: u16, mut f: impl FnMut(MatchedRuleRef), @@ -552,9 +552,9 @@ impl StyleSheet { } } - fn for_each_matched_rule( + fn for_each_matched_rule( &mut self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, sheet_style_scope: Option, sheet_index: u16, @@ -589,9 +589,9 @@ impl StyleSheet { Some(x) => x, None => return, }; - for (class, scope) in query_last.classes.iter() { - if sheet_style_scope.is_none() || sheet_style_scope == *scope { - if let Some(rules) = class_index.get(class) { + for class in query_last.classes() { + if sheet_style_scope.is_none() || sheet_style_scope == class.scope() { + if let Some(rules) = class_index.get(class.name()) { for r in rules { if let Some(selector_weight) = r.match_query(query, media_query_status, sheet_style_scope) diff --git a/float-pigment-css/src/sheet/rule.rs b/float-pigment-css/src/sheet/rule.rs index d02d02e..dda05e6 100644 --- a/float-pigment-css/src/sheet/rule.rs +++ b/float-pigment-css/src/sheet/rule.rs @@ -457,9 +457,9 @@ impl Rule { self.properties.iter() } - pub(crate) fn match_query( + pub(crate) fn match_query( &self, - query: &[StyleQuery], + query: &[T], media_query_status: &MediaQueryStatus, sheet_style_scope: Option, ) -> Option { diff --git a/float-pigment-css/src/sheet/selector.rs b/float-pigment-css/src/sheet/selector.rs index 8004d92..cb385f9 100644 --- a/float-pigment-css/src/sheet/selector.rs +++ b/float-pigment-css/src/sheet/selector.rs @@ -10,7 +10,7 @@ use cssparser::{Parser, ParserInput}; use float_pigment_css_macro::{compatibility_enum_check, compatibility_struct_check}; use crate::parser::{parse_selector, ParseState}; -use crate::query::StyleQuery; +use crate::query::{StyleNode, StyleNodeClass}; #[cfg_attr(debug_assertions, compatibility_enum_check(selector))] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] @@ -248,8 +248,13 @@ impl SelectorFragment { if !self.id.is_empty() { weight += 1 << 13; } - let class_count = self.classes.len(); - weight += (class_count.min(0xff) << 5) as u16; + let class_and_attr_count = self.classes.len() + + self + .attributes + .as_ref() + .map(|a| a.len()) + .unwrap_or_default(); + weight += (class_and_attr_count.min(0xff) << 5) as u16; if let Some(ref relation) = self.relation { weight += match &**relation { SelectorRelationType::None => 0, @@ -367,9 +372,9 @@ impl Selector { } ret } - pub(crate) fn match_query( + pub(crate) fn match_query( &self, - query: &[StyleQuery], + query: &[T], sheet_style_scope: Option, ) -> Option { let mut cur_weight = 0; @@ -378,22 +383,23 @@ impl Selector { match query.next_back() { Some(mut cur_query) => { let same_scope = sheet_style_scope.is_none() - || sheet_style_scope == cur_query.style_scope - || sheet_style_scope == cur_query.extra_style_scope; + || sheet_style_scope == cur_query.style_scope() + || sheet_style_scope == cur_query.extra_style_scope(); let mut allow_ancestor = false; let mut cur_frag = frag; loop { let mut matches = true; - if (!cur_frag.id.is_empty() && (!same_scope || cur_frag.id != cur_query.id)) + if (!cur_frag.id.is_empty() + && (!same_scope || Some(cur_frag.id.as_str()) != cur_query.id())) || (!cur_frag.tag_name.is_empty() - && (!same_scope || cur_frag.tag_name != cur_query.tag_name)) + && (!same_scope || cur_frag.tag_name != cur_query.tag_name())) { matches = false } else if let Some(pc) = cur_frag.pseudo_classes.as_ref() { match &**pc { PseudoClasses::Host => { if sheet_style_scope.is_some() - && sheet_style_scope != cur_query.host_style_scope + && sheet_style_scope != cur_query.host_style_scope() { matches = false } @@ -402,13 +408,72 @@ impl Selector { } } else { for class_name in cur_frag.classes.iter() { - if !cur_query.classes.iter().any(|x| { - (sheet_style_scope.is_none() || sheet_style_scope == x.1) - && &x.0 == class_name + if !cur_query.classes().any(|x| { + (sheet_style_scope.is_none() || sheet_style_scope == x.scope()) + && x.name() == class_name }) { matches = false; } } + + if matches { + if let Some(selector_attributes) = &cur_frag.attributes { + for attribute in selector_attributes.iter() { + let selector_attr_value = + attribute.value.as_deref().unwrap_or_default(); + if let Some(element_attr_value) = + cur_query.attribute(&attribute.name) + { + if !match attribute.operator { + AttributeOperator::Set => true, + AttributeOperator::Exact => { + element_attr_value == selector_attr_value + } + AttributeOperator::List => { + if selector_attr_value.is_empty() { + false + } else { + element_attr_value + .split(SELECTOR_WHITESPACE) + .any(|x| x == selector_attr_value) + } + } + AttributeOperator::Hyphen => { + if element_attr_value.len() + < selector_attr_value.len() + { + false + } else if element_attr_value.len() + == selector_attr_value.len() + { + element_attr_value == selector_attr_value + } else { + element_attr_value.starts_with( + &alloc::format!( + "{}-", + selector_attr_value + ), + ) + } + } + AttributeOperator::Begin => element_attr_value + .starts_with(selector_attr_value), + AttributeOperator::End => element_attr_value + .ends_with(selector_attr_value), + AttributeOperator::Contain => { + element_attr_value.contains(selector_attr_value) + } + } { + matches = false; + break; + } + } else { + matches = false; + break; + } + } + } + } } if !matches { if allow_ancestor { diff --git a/float-pigment-css/tests/media.rs b/float-pigment-css/tests/media.rs index 4203939..8c464f5 100644 --- a/float-pigment-css/tests/media.rs +++ b/float-pigment-css/tests/media.rs @@ -26,7 +26,7 @@ fn test_props( } let ssg = test_ss(&ss); let classes = vec![("m".into(), None)]; - let query = vec![StyleQuery::single(None, None, None, "", "", &classes, &[])]; + let query = vec![StyleQuery::single(None, None, None, "", "", &classes)]; let matched_rules = ssg.query_matched_rules(&query, &media); assert_eq!(matched_rules.rules.len(), 1); } @@ -70,7 +70,7 @@ fn media_type() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -103,25 +103,25 @@ fn nested() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(800., 600.), ); assert_eq!(node_properties.width(), Length::Px(3.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(801., 600.), ); assert_eq!(node_properties.width(), Length::Px(2.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(800., 601.), ); assert_eq!(node_properties.width(), Length::Px(1.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(801., 601.), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -156,7 +156,7 @@ fn decorator() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -187,7 +187,7 @@ fn min_width() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -218,7 +218,7 @@ fn max_width() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -249,7 +249,7 @@ fn min_height() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -280,7 +280,7 @@ fn max_height() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); @@ -306,13 +306,13 @@ fn width() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.width(), Length::Px(1.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(801., 600.), ); assert_eq!(node_properties.width(), Length::Px(2.)); @@ -337,13 +337,13 @@ fn height() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::::default_screen(), ); assert_eq!(node_properties.height(), Length::Px(1.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(800., 601.), ); assert_eq!(node_properties.height(), Length::Px(2.)); @@ -370,7 +370,7 @@ fn prefers_color_scheme() { media_status.theme = Theme::Light; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), media_status, ); assert_eq!(node_properties.height(), Length::Px(2.)); @@ -378,7 +378,7 @@ fn prefers_color_scheme() { media_status.theme = Theme::Dark; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), media_status, ); assert_eq!(node_properties.height(), Length::Px(1.)); @@ -386,7 +386,7 @@ fn prefers_color_scheme() { media_status.theme = Theme::None; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), media_status, ); assert_eq!(node_properties.height(), Length::Undefined); @@ -416,13 +416,13 @@ fn orientation() { let classes = vec![("a".into(), None)]; let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(800., 600.), ); assert_eq!(node_properties.height(), Length::Px(1.)); let node_properties = test_props( &ssg, - StyleQuery::single(None, None, None, "", "", &classes, &[]), + StyleQuery::single(None, None, None, "", "", &classes), MediaQueryStatus::default_screen_with_size(600., 800.), ); assert_eq!(node_properties.height(), Length::Px(2.)); diff --git a/float-pigment-css/tests/property_value.rs b/float-pigment-css/tests/property_value.rs index e70ca2b..f83459c 100644 --- a/float-pigment-css/tests/property_value.rs +++ b/float-pigment-css/tests/property_value.rs @@ -15,7 +15,7 @@ macro_rules! test_parse_stringify { let ss = StyleSheet::from_str(&style_str); ssg.append(ss); let classes = vec![("a".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let matched_rules = ssg.query_matched_rules(&vec![query], &MediaQueryStatus::::default_screen()); let mut node_properties = NodeProperties::new(None); diff --git a/float-pigment-css/tests/selector.rs b/float-pigment-css/tests/selector.rs index dfaad49..783a3ff 100644 --- a/float-pigment-css/tests/selector.rs +++ b/float-pigment-css/tests/selector.rs @@ -33,14 +33,14 @@ fn stringify() { strings.join("|") } let classes = vec![("a".into(), None), ("a2".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let matched_rules = ssg.query_matched_rules(&[query], &media_query_status); assert_eq!(merge_rule_strings(matched_rules), ".a, .b|.a|.a.a2"); let classes = vec![("a".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "d", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "d", &classes); let matched_rules = ssg.query_matched_rules(&[query], &media_query_status); assert_eq!(merge_rule_strings(matched_rules), ".a, .b|.a|#d|#d.a"); - let query = StyleQuery::single(None, None, None, "c", "d", &classes, &[]); + let query = StyleQuery::single(None, None, None, "c", "d", &classes); let matched_rules = ssg.query_matched_rules(&[query], &media_query_status); assert_eq!( merge_rule_strings(matched_rules), @@ -62,14 +62,14 @@ fn rule_stringify() { ssg.append(ss); let media_query_status = MediaQueryStatus::::default_screen(); let classes = vec![("a".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let matched_rules = ssg.query_matched_rules(&[query], &media_query_status); assert_eq!( matched_rules.rules[0].rule.to_string(), ".a { display: block; }" ); let classes = vec![("b".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let matched_rules = ssg.query_matched_rules(&[query], &media_query_status); assert_eq!( matched_rules.rules[0].rule.to_string(), @@ -85,7 +85,7 @@ fn multi_classes() { "#]); { let classes = vec![("a".into(), None)]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, @@ -272,23 +272,26 @@ fn attribute_selector() { let mut ssg = StyleSheetGroup::new(); let ss = StyleSheet::from_str( r#" - a[title] { height: 100px} + a[title] { height: 100px } .b { height: 200px } a[class~="logo"] { width: 300px } - .c { height: 400px } + .c { height: 400px !important; } "#, ); ssg.append(ss); let node_properties = query(&ssg, "a", "", [""], []); - assert_ne!(node_properties.height(), Length::Undefined); - assert_ne!(node_properties.width(), Length::Undefined); - // let node_properties = query(&ssg, "a", "", [""], Some(vec!["title".into()])); - // assert_ne!(node_properties.height(), Length::Px(100.)); - // assert_ne!(node_properties.width(), Length::Undefined); - let node_properties = query(&ssg, "", "", ["b"], []); - assert_eq!(node_properties.height(), Length::Px(200.)); - let node_properties = query(&ssg, "", "", ["c"], []); - assert_eq!(node_properties.height(), Length::Px(400.)); + assert_eq!(node_properties.height(), Length::Undefined); + assert_eq!(node_properties.width(), Length::Undefined); + let node_properties = query(&ssg, "a", "", [""], [("title".into(), "".into()), ("class".into(), "logo1 logo2".into())]); + assert_eq!(node_properties.height(), Length::Px(100.)); + assert_eq!(node_properties.width(), Length::Undefined); + let node_properties = query(&ssg, "a", "", [""], [("title".into(), "".into()), ("class".into(), "logo1 logo logo2".into())]); + assert_eq!(node_properties.height(), Length::Px(100.)); + assert_eq!(node_properties.width(), Length::Px(300.)); + let node_properties = query(&ssg, "a", "", ["b"], [("title".into(), "".into())]); + assert_eq!(node_properties.height(), Length::Px(100.)); + let node_properties = query(&ssg, "", "", [], [("title".into(), "".into())]); + assert_eq!(node_properties.height(), Length::Undefined); } #[test] diff --git a/float-pigment-css/tests/style_sheet_group.rs b/float-pigment-css/tests/style_sheet_group.rs index fb83d0f..b1b83da 100644 --- a/float-pigment-css/tests/style_sheet_group.rs +++ b/float-pigment-css/tests/style_sheet_group.rs @@ -338,7 +338,7 @@ fn scopes_in_tag_name_and_id() { ssg.append_from_resource(&ssr, "file1", NonZeroUsize::new(1)); ssg.append_from_resource(&ssr, "file2", NonZeroUsize::new(2)); let classes = vec![]; - let query = StyleQuery::single(None, None, None, "view", "i", &classes, &[]); + let query = StyleQuery::single(None, None, None, "view", "i", &classes); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, @@ -347,18 +347,30 @@ fn scopes_in_tag_name_and_id() { ); assert_eq!(node_properties.width(), Length::Px(0.)); assert_eq!(node_properties.height(), Length::Undefined); - let query = - StyleQuery::single(NonZeroUsize::new(1), None, None, "view", "i", &classes, &[]); + let query = StyleQuery::single( + NonZeroUsize::new(1), + None, + None, + "view", + "i", + &classes, + ); let mut node_properties = NodeProperties::new(None); ssg.query_single( - &query, + query, &MediaQueryStatus::::default_screen(), &mut node_properties, ); assert_eq!(node_properties.width(), Length::Px(1.)); assert_eq!(node_properties.height(), Length::Undefined); - let query = - StyleQuery::single(NonZeroUsize::new(2), None, None, "view", "i", &classes, &[]); + let query = StyleQuery::single( + NonZeroUsize::new(2), + None, + None, + "view", + "i", + &classes, + ); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, @@ -400,7 +412,7 @@ fn scopes_in_classes() { ssg.append_from_resource(&ssr, "file1", NonZeroUsize::new(1)); ssg.append_from_resource(&ssr, "file2", NonZeroUsize::new(2)); let classes = vec![("a".into(), NonZeroUsize::new(0))]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, @@ -410,7 +422,7 @@ fn scopes_in_classes() { assert_eq!(node_properties.width(), Length::Px(0.)); assert_eq!(node_properties.height(), Length::Undefined); let classes = vec![("a".into(), NonZeroUsize::new(1))]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, @@ -420,7 +432,7 @@ fn scopes_in_classes() { assert_eq!(node_properties.width(), Length::Px(1.)); assert_eq!(node_properties.height(), Length::Undefined); let classes = vec![("a".into(), NonZeroUsize::new(2))]; - let query = StyleQuery::single(None, None, None, "", "", &classes, &[]); + let query = StyleQuery::single(None, None, None, "", "", &classes); let mut node_properties = NodeProperties::new(None); ssg.query_single( &query, diff --git a/float-pigment-css/tests/utils/mod.rs b/float-pigment-css/tests/utils/mod.rs index da8480b..e2eeb2d 100644 --- a/float-pigment-css/tests/utils/mod.rs +++ b/float-pigment-css/tests/utils/mod.rs @@ -3,6 +3,79 @@ use std::num::NonZeroUsize; use float_pigment_css::{ length_num::LengthNum, property::*, MediaQueryStatus, StyleQuery, StyleSheet, StyleSheetGroup, }; +use float_pigment_css::query::{StyleNode}; + +pub struct StyleQueryTest<'a> { + pub style_scope: Option, + pub extra_style_scope: Option, + pub host_style_scope: Option, + pub tag_name: &'a str, + pub id: &'a str, + pub classes: &'a [(String, Option)], + pub attributes: &'a [(String, String)], +} + +impl<'a> StyleNode for StyleQueryTest<'a> { + type Class = (String, Option); + type ClassIter<'c> + = core::slice::Iter<'c, Self::Class> + where + 'a: 'c; + + fn style_scope(&self) -> Option { + self.style_scope + } + + fn extra_style_scope(&self) -> Option { + self.extra_style_scope + } + + fn host_style_scope(&self) -> Option { + self.host_style_scope + } + + fn tag_name(&self) -> &str { + self.tag_name + } + + fn id(&self) -> Option<&str> { + Some(self.id) + } + + fn classes(&self) -> Self::ClassIter<'_> { + self.classes.iter() + } + + fn attribute(&self, name: &str) -> Option<&str> { + self.attributes + .iter() + .find(|(n, _)| n == name) + .map(|(_, v)| v.as_str()) + } +} + +impl<'a> StyleQueryTest<'a> { + pub fn single( + style_scope: Option, + extra_style_scope: Option, + host_style_scope: Option, + tag_name: &'a str, + id: &'a str, + classes: &'a [(String, Option)], + attributes: &'a [(String, String)], + ) -> Self { + Self { + style_scope, + extra_style_scope, + host_style_scope, + tag_name, + id, + classes, + attributes, + } + } +} + #[macro_export] macro_rules! test_parse_property { ($prop: ident, $prop_name: expr, $str_value: expr, $value: expr) => {{ @@ -34,7 +107,7 @@ pub fn query( tag_name: &str, id: &str, classes: [&str; N], - attributes: [&str; M], + attributes: [(String, String); M], ) -> NodeProperties { query_with_media( ssg, @@ -52,17 +125,16 @@ pub(super) fn query_with_media( tag_name: &str, id: &str, classes: [&str; N], - attributes: [&str; M], + attributes: [(String, String); M], media_query_status: &MediaQueryStatus, ) -> NodeProperties { let classes = classes .iter() .map(|x| (x.to_string(), None)) .collect::>(); - let attributes = attributes.iter().map(|x| x.to_string()).collect::>(); - let query = StyleQuery::single(None, None, None, tag_name, id, &classes, &attributes); + let query = StyleQueryTest::single(None, None, None, tag_name, id, &classes, &attributes); let mut node_properties = NodeProperties::new(None); - ssg.query_single(&query, media_query_status, &mut node_properties); + ssg.query_single(query, media_query_status, &mut node_properties); node_properties } @@ -70,7 +142,7 @@ pub(super) struct StyleQueryWrapper { tag_name: String, id: String, classes: Vec<(String, Option)>, - attributes: Vec, + attributes: Vec<(String, String)>, } #[allow(dead_code)] @@ -78,15 +150,14 @@ pub(super) fn query_item<'a, const N: usize, const M: usize>( tag_name: &'a str, id: &'a str, classes: [&'a str; N], - attributes: [&'a str; M], + attributes: [(String, String); M], ) -> StyleQueryWrapper { let classes = classes.iter().map(|x| (x.to_string(), None)).collect(); - let attributes = attributes.iter().map(|x| x.to_string()).collect(); StyleQueryWrapper { tag_name: tag_name.to_owned(), id: id.to_owned(), classes, - attributes, + attributes: attributes.to_vec(), } } @@ -108,7 +179,7 @@ pub(super) fn query_list_with_media( let query: Vec<_> = list .iter() .map(|sqw| { - StyleQuery::single( + StyleQueryTest::single( None, None, None, @@ -134,7 +205,7 @@ pub(super) fn query_list_with_parent( let query: Vec<_> = list .iter() .map(|sqw| { - StyleQuery::single( + StyleQueryTest::single( None, None, None,