lewp_css/domain/at_rules/media/
media_query.rs1use {
5 super::{MediaQueryType, Qualifier},
6 crate::{
7 domain::at_rules::media::MediaExpression,
8 parsers::{
9 separators::{Comma, Separated},
10 ParserContext,
11 },
12 CustomParseError,
13 },
14 cssparser::{ParseError, Parser, ToCss},
15 either::{Left, Right},
16 std::fmt::{self},
17};
18
19#[derive(Clone, Debug, PartialEq)]
23pub struct MediaQuery {
24 pub qualifier: Option<Qualifier>,
26
27 pub media_type: MediaQueryType,
29
30 pub expressions: Vec<MediaExpression>,
32}
33
34impl Separated for MediaQuery {
35 type Delimiter = Comma;
36}
37
38impl ToCss for MediaQuery {
39 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
40 if let Some(qualifier) = self.qualifier {
41 qualifier.to_css(dest)?;
42 dest.write_char(' ')?;
43 }
44
45 use self::MediaQueryType::*;
46
47 match self.media_type {
48 All => {
49 if self.qualifier.is_some() || self.expressions.is_empty() {
52 dest.write_str("all")?;
53 }
54 }
55
56 Concrete(ref value) => value.to_css(dest)?,
57 }
58
59 if self.expressions.is_empty() {
60 return Ok(());
61 }
62
63 if self.media_type != All || self.qualifier.is_some() {
64 dest.write_str(" and ")?;
65 }
66
67 self.expressions[0].to_css(dest)?;
68 for expression in self.expressions.iter().skip(1) {
69 dest.write_str(" and ")?;
70 expression.to_css(dest)?;
71 }
72
73 Ok(())
74 }
75}
76
77impl MediaQuery {
78 #[inline(always)]
80 pub(crate) fn never_matching() -> Self {
81 Self {
82 qualifier: Some(Qualifier::Not),
83 media_type: MediaQueryType::All,
84 expressions: vec![],
85 }
86 }
87
88 pub(crate) fn parse<'i, 't>(
92 context: &ParserContext,
93 input: &mut Parser<'i, 't>,
94 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
95 let mut expressions = vec![];
96
97 use self::Qualifier::*;
98
99 let qualifier = if input
100 .r#try(|input| input.expect_ident_matching("only"))
101 .is_ok()
102 {
103 Some(Only)
104 } else if input
105 .r#try(|input| input.expect_ident_matching("not"))
106 .is_ok()
107 {
108 Some(Not)
109 } else {
110 None
111 };
112
113 let isThereAValidMediaType = input.r#try(|input| {
114 if let Ok(ident) = input.expect_ident() {
115 match MediaQueryType::parse(ident).map_err(ParseError::from) {
116 Ok(mediaType) => Ok(Left(mediaType)),
117 Err(error) => Ok(Right(error)),
118 }
119 } else {
120 Err(())
121 }
122 });
123
124 let media_type = match isThereAValidMediaType {
125 Ok(Left(media_type)) => media_type,
126
127 Ok(Right(error)) => return Err(error),
128
129 Err(()) => {
130 if qualifier.is_some() {
132 return Err(ParseError::from(
133 CustomParseError::MediaTypeIsOnlyOptionalIfQualifiedIsNotSpecified,
134 ));
135 }
136
137 expressions.push(MediaExpression::parse(context, input)?);
139
140 MediaQueryType::All
141 }
142 };
143
144 loop {
146 if input
147 .r#try(|input| input.expect_ident_matching("and"))
148 .is_err()
149 {
150 return Ok(Self {
151 qualifier,
152 media_type,
153 expressions,
154 });
155 }
156 expressions.push(MediaExpression::parse(context, input)?)
157 }
158 }
159}