@@ -10,9 +10,30 @@ use crate::{
1010 utils:: to_camel_case,
1111} ;
1212
13+ #[ derive(
14+ Debug , PartialEq , PartialOrd , Ord , Clone , Copy , Hash , Eq , Serialize , Deserialize , Default ,
15+ ) ]
16+ pub enum AtRuleKind {
17+ #[ default]
18+ Media ,
19+ Supports ,
20+ Container ,
21+ }
22+
23+ impl Display for AtRuleKind {
24+ fn fmt ( & self , f : & mut Formatter ) -> std:: fmt:: Result {
25+ match self {
26+ AtRuleKind :: Media => write ! ( f, "media" ) ,
27+ AtRuleKind :: Supports => write ! ( f, "supports" ) ,
28+ AtRuleKind :: Container => write ! ( f, "container" ) ,
29+ }
30+ }
31+ }
32+
1333#[ derive( Debug , PartialEq , Clone , Hash , Eq , Serialize , Deserialize ) ]
1434pub enum StyleSelector {
15- Media {
35+ At {
36+ kind : AtRuleKind ,
1637 query : String ,
1738 selector : Option < String > ,
1839 } ,
@@ -30,7 +51,12 @@ fn optimize_selector_string(selector: &str) -> String {
3051}
3152pub fn optimize_selector ( selector : StyleSelector ) -> StyleSelector {
3253 match selector {
33- StyleSelector :: Media { query, selector } => StyleSelector :: Media {
54+ StyleSelector :: At {
55+ kind,
56+ query,
57+ selector,
58+ } => StyleSelector :: At {
59+ kind,
3460 query : query. to_string ( ) ,
3561 selector : selector
3662 . as_ref ( )
@@ -54,15 +80,21 @@ impl Ord for StyleSelector {
5480 fn cmp ( & self , other : & Self ) -> Ordering {
5581 match ( self , other) {
5682 (
57- StyleSelector :: Media {
83+ StyleSelector :: At {
84+ kind : ka,
5885 query : a,
5986 selector : aa,
6087 } ,
61- StyleSelector :: Media {
88+ StyleSelector :: At {
89+ kind : kb,
6290 query : b,
6391 selector : bb,
6492 } ,
6593 ) => {
94+ let k = ( * ka as u8 ) . cmp ( & ( * kb as u8 ) ) ;
95+ if k != Ordering :: Equal {
96+ return k;
97+ }
6698 let c = a. cmp ( b) ;
6799 if c == Ordering :: Equal { aa. cmp ( bb) } else { c }
68100 }
@@ -74,20 +106,8 @@ impl Ord for StyleSelector {
74106 order_cmp
75107 }
76108 }
77- (
78- StyleSelector :: Media {
79- selector : _,
80- query : _,
81- } ,
82- StyleSelector :: Selector ( _) ,
83- ) => Ordering :: Greater ,
84- (
85- StyleSelector :: Selector ( _) ,
86- StyleSelector :: Media {
87- selector : _,
88- query : _,
89- } ,
90- ) => Ordering :: Less ,
109+ ( StyleSelector :: At { .. } , StyleSelector :: Selector ( _) ) => Ordering :: Greater ,
110+ ( StyleSelector :: Selector ( _) , StyleSelector :: At { .. } ) => Ordering :: Less ,
91111 ( StyleSelector :: Global ( a, _) , StyleSelector :: Global ( b, _) ) => {
92112 if a == b {
93113 return Ordering :: Equal ;
@@ -143,9 +163,10 @@ impl From<&str> for StyleSelector {
143163 } else if let Some ( s) = value. strip_prefix ( "theme-" ) {
144164 // first character should lower case
145165 StyleSelector :: Selector ( format ! ( ":root[data-theme={}] &" , to_camel_case( s) ) )
146- } else if value == "print" {
147- StyleSelector :: Media {
148- query : "print" . to_string ( ) ,
166+ } else if matches ! ( value. as_str( ) , "print" | "screen" | "speech" | "all" ) {
167+ StyleSelector :: At {
168+ kind : AtRuleKind :: Media ,
169+ query : value. to_string ( ) ,
149170 selector : None ,
150171 }
151172 } else {
@@ -201,11 +222,16 @@ impl Display for StyleSelector {
201222 "{}" ,
202223 match self {
203224 StyleSelector :: Selector ( value) => value. to_string( ) ,
204- StyleSelector :: Media { query, selector } => {
225+ StyleSelector :: At {
226+ kind,
227+ query,
228+ selector,
229+ } => {
230+ let space = if query. starts_with( '(' ) { "" } else { " " } ;
205231 if let Some ( selector) = selector {
206- format!( "@{query} {selector}" )
232+ format!( "@{kind}{space}{ query} {selector}" )
207233 } else {
208- format!( "@{query}" )
234+ format!( "@{kind}{space}{ query}" )
209235 }
210236 }
211237 StyleSelector :: Global ( value, _) => value. to_string( ) ,
@@ -255,11 +281,33 @@ mod tests {
255281
256282 #[ rstest]
257283 #[ case( StyleSelector :: Selector ( "&:hover" . to_string( ) ) , "&:hover" ) ]
258- #[ case( StyleSelector :: Media {
284+ #[ case( StyleSelector :: At {
285+ kind: AtRuleKind :: Media ,
259286 query: "screen and (max-width: 600px)" . to_string( ) ,
260287 selector: None ,
261288 } ,
262- "@screen and (max-width: 600px)"
289+ "@media screen and (max-width: 600px)"
290+ ) ]
291+ #[ case( StyleSelector :: At {
292+ kind: AtRuleKind :: Supports ,
293+ query: "(display: grid)" . to_string( ) ,
294+ selector: None ,
295+ } ,
296+ "@supports(display: grid)"
297+ ) ]
298+ #[ case( StyleSelector :: At {
299+ kind: AtRuleKind :: Container ,
300+ query: "(min-width: 768px)" . to_string( ) ,
301+ selector: None ,
302+ } ,
303+ "@container(min-width: 768px)"
304+ ) ]
305+ #[ case( StyleSelector :: At {
306+ kind: AtRuleKind :: Container ,
307+ query: "sidebar (min-width: 400px)" . to_string( ) ,
308+ selector: None ,
309+ } ,
310+ "@container sidebar (min-width: 400px)"
263311 ) ]
264312 #[ case( StyleSelector :: Global ( ":root[data-theme=dark]" . to_string( ) , "file.rs" . to_string( ) ) , ":root[data-theme=dark]" ) ]
265313 fn test_style_selector_display ( #[ case] selector : StyleSelector , #[ case] expected : & str ) {
@@ -269,7 +317,8 @@ mod tests {
269317
270318 #[ rstest]
271319 #[ case(
272- StyleSelector :: Media {
320+ StyleSelector :: At {
321+ kind: AtRuleKind :: Media ,
273322 query: "screen" . to_string( ) ,
274323 selector: None ,
275324 } ,
@@ -282,16 +331,31 @@ mod tests {
282331 std:: cmp:: Ordering :: Less
283332 ) ]
284333 #[ case(
285- StyleSelector :: Media {
334+ StyleSelector :: At {
335+ kind: AtRuleKind :: Media ,
286336 query: "a" . to_string( ) ,
287337 selector: None ,
288338 } ,
289- StyleSelector :: Media {
339+ StyleSelector :: At {
340+ kind: AtRuleKind :: Media ,
290341 query: "b" . to_string( ) ,
291342 selector: None ,
292343 } ,
293344 std:: cmp:: Ordering :: Less
294345 ) ]
346+ #[ case(
347+ StyleSelector :: At {
348+ kind: AtRuleKind :: Media ,
349+ query: "(min-width: 768px)" . to_string( ) ,
350+ selector: None ,
351+ } ,
352+ StyleSelector :: At {
353+ kind: AtRuleKind :: Supports ,
354+ query: "(display: grid)" . to_string( ) ,
355+ selector: None ,
356+ } ,
357+ std:: cmp:: Ordering :: Less
358+ ) ]
295359 #[ case(
296360 StyleSelector :: Global ( ":root[data-theme=dark]" . to_string( ) , "file1.rs" . to_string( ) ) ,
297361 StyleSelector :: Global ( ":root[data-theme=light]" . to_string( ) , "file2.rs" . to_string( ) ) ,
@@ -304,7 +368,8 @@ mod tests {
304368 ) ]
305369 #[ case(
306370 StyleSelector :: Selector ( "&:hover" . to_string( ) ) ,
307- StyleSelector :: Media {
371+ StyleSelector :: At {
372+ kind: AtRuleKind :: Media ,
308373 query: "screen" . to_string( ) ,
309374 selector: None ,
310375 } ,
0 commit comments