Skip to content

Commit 7f0bd79

Browse files
authored
Merge pull request #5 from wechat-miniprogram/feat-attr
feat: impl attribute matching
2 parents 496df92 + 25f9ea8 commit 7f0bd79

File tree

11 files changed

+464
-102
lines changed

11 files changed

+464
-102
lines changed

float-pigment-css/src/group.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -790,21 +790,21 @@ impl StyleSheetGroup {
790790
/// Query a single node selector (usually for testing only).
791791
///
792792
/// Note that the font size and `em` values will be converted to `px` values.
793-
pub fn query_single<L: LengthNum>(
793+
pub fn query_single<L: LengthNum, T: StyleNode>(
794794
&self,
795-
query: &StyleQuery,
795+
query: T,
796796
media_query_status: &MediaQueryStatus<L>,
797797
node_properties: &mut NodeProperties,
798798
) {
799-
self.query_ancestor_path(&[query.clone()], media_query_status, node_properties, None)
799+
self.query_ancestor_path(&[query], media_query_status, node_properties, None)
800800
}
801801

802802
/// Find rules that matches the query.
803803
///
804804
/// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself.
805-
pub fn for_each_matched_rule<L: LengthNum>(
805+
pub fn for_each_matched_rule<L: LengthNum, T: StyleNode>(
806806
&self,
807-
query: &[StyleQuery],
807+
query: &[T],
808808
media_query_status: &MediaQueryStatus<L>,
809809
mut f: impl FnMut(MatchedRuleRef, Option<&LinkedStyleSheet>),
810810
) {
@@ -824,9 +824,9 @@ impl StyleSheetGroup {
824824
/// Get a rule list that matches the query.
825825
///
826826
/// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself.
827-
pub fn query_matched_rules<L: LengthNum>(
827+
pub fn query_matched_rules<L: LengthNum, T: StyleNode>(
828828
&self,
829-
query: &[StyleQuery],
829+
query: &[T],
830830
media_query_status: &MediaQueryStatus<L>,
831831
) -> MatchedRuleList {
832832
let mut rules = vec![];
@@ -845,9 +845,9 @@ impl StyleSheetGroup {
845845
///
846846
/// The query is a `&[StyleQuery]` which means all selector information of the ancestors and the node itself.
847847
/// Note that the font size and `em` values will be converted to `px` values.
848-
pub fn query_ancestor_path<L: LengthNum>(
848+
pub fn query_ancestor_path<L: LengthNum, T: StyleNode>(
849849
&self,
850-
query: &[StyleQuery],
850+
query: &[T],
851851
media_query_status: &MediaQueryStatus<L>,
852852
node_properties: &mut NodeProperties,
853853
parent_node_properties: Option<&NodeProperties>,

float-pigment-css/src/parser/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1611,15 +1611,15 @@ fn parse_attribute_selector<'a, 't: 'a, 'i: 't>(
16111611
// [name=value]
16121612
Ok(&Token::Delim('=')) => AttributeOperator::Exact,
16131613
// [name~=value]
1614-
Ok(&Token::IncludeMatch) => AttributeOperator::Contain,
1614+
Ok(&Token::IncludeMatch) => AttributeOperator::List,
16151615
// [name|=value]
16161616
Ok(&Token::DashMatch) => AttributeOperator::Hyphen,
16171617
// [name^=value]
16181618
Ok(&Token::PrefixMatch) => AttributeOperator::Begin,
16191619
// [name$=value]
16201620
Ok(&Token::SuffixMatch) => AttributeOperator::End,
16211621
// [name*=value]
1622-
Ok(&Token::SubstringMatch) => AttributeOperator::List,
1622+
Ok(&Token::SubstringMatch) => AttributeOperator::Contain,
16231623
Ok(_) => {
16241624
return Err(location.new_custom_error(CustomError::UnexpectedTokenInAttributeSelector))
16251625
}

float-pigment-css/src/query.rs

Lines changed: 159 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,103 @@ impl<L: LengthNum> MediaQueryStatus<L> {
8585
}
8686
}
8787

88+
pub trait StyleNodeClass {
89+
fn name(&self) -> &str;
90+
fn scope(&self) -> Option<NonZeroUsize>;
91+
}
92+
93+
impl StyleNodeClass for (String, Option<NonZeroUsize>) {
94+
fn name(&self) -> &str {
95+
&self.0
96+
}
97+
98+
fn scope(&self) -> Option<NonZeroUsize> {
99+
self.1
100+
}
101+
}
102+
103+
pub enum StyleNodeAttributeCaseSensitivity {
104+
CaseSensitive,
105+
CaseInsensitive,
106+
}
107+
108+
impl StyleNodeAttributeCaseSensitivity {
109+
pub fn eq(&self, a: &str, b: &str) -> bool {
110+
match self {
111+
Self::CaseSensitive => a == b,
112+
Self::CaseInsensitive => a.eq_ignore_ascii_case(b),
113+
}
114+
}
115+
116+
pub fn starts_with(&self, a: &str, b: &str) -> bool {
117+
// FIXME: reduce memory allocation
118+
match self {
119+
Self::CaseSensitive => a.starts_with(b),
120+
Self::CaseInsensitive => a.to_ascii_lowercase().starts_with(&b.to_ascii_lowercase()),
121+
}
122+
}
123+
124+
pub fn ends_with(&self, a: &str, b: &str) -> bool {
125+
// FIXME: reduce memory allocation
126+
match self {
127+
Self::CaseSensitive => a.ends_with(b),
128+
Self::CaseInsensitive => a.to_ascii_lowercase().ends_with(&b.to_ascii_lowercase()),
129+
}
130+
}
131+
132+
pub fn contains(&self, a: &str, b: &str) -> bool {
133+
// FIXME: reduce memory allocation
134+
match self {
135+
Self::CaseSensitive => a.contains(b),
136+
Self::CaseInsensitive => a.to_ascii_lowercase().contains(&b.to_ascii_lowercase()),
137+
}
138+
}
139+
}
140+
141+
pub trait StyleNode {
142+
type Class: StyleNodeClass;
143+
type ClassIter<'a>: Iterator<Item = &'a Self::Class>
144+
where
145+
Self: 'a;
146+
147+
fn style_scope(&self) -> Option<NonZeroUsize>;
148+
fn extra_style_scope(&self) -> Option<NonZeroUsize>;
149+
fn host_style_scope(&self) -> Option<NonZeroUsize>;
150+
fn tag_name(&self) -> &str;
151+
fn id(&self) -> Option<&str>;
152+
fn classes(&self) -> Self::ClassIter<'_>;
153+
fn attribute(&self, name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)>;
154+
155+
fn contain_scope(&self, scope: Option<NonZeroUsize>) -> bool {
156+
scope.is_none()
157+
|| self.style_scope() == scope
158+
|| self.extra_style_scope() == scope
159+
|| self.host_style_scope() == scope
160+
}
161+
}
162+
88163
/// Represents node information, used for matching rules.
89-
#[derive(Clone, Debug)]
164+
#[derive(Debug)]
90165
pub struct StyleQuery<'a> {
91166
pub(super) style_scope: Option<NonZeroUsize>,
92167
pub(super) extra_style_scope: Option<NonZeroUsize>,
93168
pub(super) host_style_scope: Option<NonZeroUsize>,
94169
pub(super) tag_name: &'a str,
95170
pub(super) id: &'a str,
96171
pub(super) classes: &'a [(String, Option<NonZeroUsize>)],
97-
#[allow(unused)]
98-
pub(super) attributes: &'a [String], // TODO support attributes
172+
}
173+
174+
impl Clone for StyleQuery<'_> {
175+
fn clone(&self) -> Self {
176+
Self {
177+
style_scope: self.style_scope,
178+
extra_style_scope: self.extra_style_scope,
179+
host_style_scope: self.host_style_scope,
180+
tag_name: self.tag_name,
181+
id: self.id,
182+
classes: self.classes,
183+
}
184+
}
99185
}
100186

101187
impl<'a> StyleQuery<'a> {
@@ -107,7 +193,6 @@ impl<'a> StyleQuery<'a> {
107193
tag_name: &'a str,
108194
id: &'a str,
109195
classes: &'a [(String, Option<NonZeroUsize>)],
110-
attributes: &'a [String],
111196
) -> Self {
112197
Self {
113198
style_scope,
@@ -116,15 +201,79 @@ impl<'a> StyleQuery<'a> {
116201
tag_name,
117202
id,
118203
classes,
119-
attributes,
120204
}
121205
}
206+
}
122207

123-
pub(crate) fn contain_scope(&self, scope: Option<NonZeroUsize>) -> bool {
124-
scope.is_none()
125-
|| self.style_scope == scope
126-
|| self.extra_style_scope == scope
127-
|| self.host_style_scope == scope
208+
impl<'a> StyleNode for StyleQuery<'a> {
209+
type Class = (String, Option<NonZeroUsize>);
210+
type ClassIter<'c>
211+
= core::slice::Iter<'c, Self::Class>
212+
where
213+
'a: 'c;
214+
215+
fn style_scope(&self) -> Option<NonZeroUsize> {
216+
self.style_scope
217+
}
218+
219+
fn extra_style_scope(&self) -> Option<NonZeroUsize> {
220+
self.extra_style_scope
221+
}
222+
223+
fn host_style_scope(&self) -> Option<NonZeroUsize> {
224+
self.host_style_scope
225+
}
226+
227+
fn tag_name(&self) -> &str {
228+
self.tag_name
229+
}
230+
231+
fn id(&self) -> Option<&str> {
232+
Some(self.id)
233+
}
234+
235+
fn classes(&self) -> Self::ClassIter<'_> {
236+
self.classes.iter()
237+
}
238+
239+
fn attribute(&self, name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
240+
None
241+
}
242+
}
243+
244+
impl<'b, 'a: 'b> StyleNode for &'b StyleQuery<'a> {
245+
type Class = (String, Option<NonZeroUsize>);
246+
type ClassIter<'c>
247+
= core::slice::Iter<'c, Self::Class>
248+
where
249+
'b: 'c;
250+
251+
fn style_scope(&self) -> Option<NonZeroUsize> {
252+
self.style_scope
253+
}
254+
255+
fn extra_style_scope(&self) -> Option<NonZeroUsize> {
256+
self.extra_style_scope
257+
}
258+
259+
fn host_style_scope(&self) -> Option<NonZeroUsize> {
260+
self.host_style_scope
261+
}
262+
263+
fn tag_name(&self) -> &str {
264+
self.tag_name
265+
}
266+
267+
fn id(&self) -> Option<&str> {
268+
Some(self.id)
269+
}
270+
271+
fn classes(&self) -> Self::ClassIter<'_> {
272+
self.classes.iter()
273+
}
274+
275+
fn attribute(&self, name: &str) -> Option<(&str, StyleNodeAttributeCaseSensitivity)> {
276+
None
128277
}
129278
}
130279

float-pigment-css/src/sheet/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,9 @@ impl LinkedStyleSheet {
365365
Err(rule)
366366
}
367367

368-
pub(crate) fn for_each_matched_rule<L: LengthNum>(
368+
pub(crate) fn for_each_matched_rule<L: LengthNum, T: StyleNode>(
369369
&self,
370-
query: &[StyleQuery],
370+
query: &[T],
371371
media_query_status: &MediaQueryStatus<L>,
372372
sheet_index: u16,
373373
mut f: impl FnMut(MatchedRuleRef),
@@ -552,9 +552,9 @@ impl StyleSheet {
552552
}
553553
}
554554

555-
fn for_each_matched_rule<L: LengthNum>(
555+
fn for_each_matched_rule<L: LengthNum, T: StyleNode>(
556556
&mut self,
557-
query: &[StyleQuery],
557+
query: &[T],
558558
media_query_status: &MediaQueryStatus<L>,
559559
sheet_style_scope: Option<NonZeroUsize>,
560560
sheet_index: u16,
@@ -589,9 +589,9 @@ impl StyleSheet {
589589
Some(x) => x,
590590
None => return,
591591
};
592-
for (class, scope) in query_last.classes.iter() {
593-
if sheet_style_scope.is_none() || sheet_style_scope == *scope {
594-
if let Some(rules) = class_index.get(class) {
592+
for class in query_last.classes() {
593+
if sheet_style_scope.is_none() || sheet_style_scope == class.scope() {
594+
if let Some(rules) = class_index.get(class.name()) {
595595
for r in rules {
596596
if let Some(selector_weight) =
597597
r.match_query(query, media_query_status, sheet_style_scope)

float-pigment-css/src/sheet/rule.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -457,9 +457,9 @@ impl Rule {
457457
self.properties.iter()
458458
}
459459

460-
pub(crate) fn match_query<L: LengthNum>(
460+
pub(crate) fn match_query<L: LengthNum, T: StyleNode>(
461461
&self,
462-
query: &[StyleQuery],
462+
query: &[T],
463463
media_query_status: &MediaQueryStatus<L>,
464464
sheet_style_scope: Option<NonZeroUsize>,
465465
) -> Option<u16> {

0 commit comments

Comments
 (0)