rama_http/protocols/html/selector/
builder.rs1use super::ast::{
12 AttributeOperator, AttributeSelector, CaseSensitivity, Combinator, ComplexSelector, Compound,
13 LocalName, Nth, NthType, Selector, SelectorPart,
14};
15
16#[inline]
21fn debug_assert_ident(kind: &str, value: &str) {
22 debug_assert!(
23 !value.is_empty() && !value.bytes().any(|b| b.is_ascii_whitespace()),
24 "{kind} must be a non-empty literal without whitespace, got {value:?} \
25 (use `parse` for a selector fragment)"
26 );
27}
28
29impl Selector {
30 #[must_use]
33 pub fn tag(name: impl AsRef<str>) -> Self {
34 Self::of(Compound::tag(name))
35 }
36
37 #[must_use]
39 pub fn class(name: impl AsRef<str>) -> Self {
40 Self::of(Compound::class(name))
41 }
42
43 #[must_use]
45 pub fn id(name: impl AsRef<str>) -> Self {
46 Self::of(Compound::id(name))
47 }
48
49 #[must_use]
51 pub fn any() -> Self {
52 Self::of(Compound::any())
53 }
54
55 #[must_use]
57 pub fn of(compound: Compound) -> Self {
58 Self {
59 selectors: vec![ComplexSelector {
60 parts: vec![SelectorPart {
61 combinator: None,
62 compound,
63 }],
64 }],
65 }
66 }
67
68 #[must_use]
71 pub fn child(self, compound: Compound) -> Self {
72 self.combine(Combinator::Child, compound)
73 }
74
75 #[must_use]
78 pub fn descendant(self, compound: Compound) -> Self {
79 self.combine(Combinator::Descendant, compound)
80 }
81
82 #[must_use]
85 pub fn or(mut self, other: Self) -> Self {
86 self.selectors.extend(other.selectors);
87 self
88 }
89
90 fn combine(mut self, combinator: Combinator, compound: Compound) -> Self {
91 if let Some(complex) = self.selectors.last_mut() {
94 complex.parts.push(SelectorPart {
95 combinator: Some(combinator),
96 compound,
97 });
98 }
99 self
100 }
101}
102
103impl Compound {
104 #[must_use]
106 pub fn tag(name: impl AsRef<str>) -> Self {
107 let name = name.as_ref();
108 debug_assert_ident("tag name", name);
109 Self {
110 name: Some(LocalName::new(name)),
111 ..Self::default()
112 }
113 }
114
115 #[must_use]
117 pub fn any() -> Self {
118 Self {
119 explicit_universal: true,
120 ..Self::default()
121 }
122 }
123
124 #[must_use]
126 pub fn class(name: impl AsRef<str>) -> Self {
127 Self::default().with_class(name)
128 }
129
130 #[must_use]
132 pub fn id(name: impl AsRef<str>) -> Self {
133 Self::default().with_id(name)
134 }
135
136 #[must_use]
138 pub fn with_id(mut self, id: impl AsRef<str>) -> Self {
139 let id = id.as_ref();
140 debug_assert_ident("id", id);
141 self.id = Some(id.into());
142 self
143 }
144
145 #[must_use]
147 pub fn with_class(mut self, class: impl AsRef<str>) -> Self {
148 let class = class.as_ref();
149 debug_assert_ident("class", class);
150 self.classes.push(class.into());
151 self
152 }
153
154 #[must_use]
156 pub fn with_attr(self, name: impl AsRef<str>) -> Self {
157 self.push_attr(name, None, "", CaseSensitivity::CaseSensitive)
158 }
159
160 #[must_use]
162 pub fn with_attr_eq(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
163 self.push_attr(
164 name,
165 Some(AttributeOperator::Equals),
166 value.as_ref(),
167 CaseSensitivity::CaseSensitive,
168 )
169 }
170
171 #[must_use]
173 pub fn with_attr_eq_ignore_case(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
174 self.push_attr(
175 name,
176 Some(AttributeOperator::Equals),
177 value.as_ref(),
178 CaseSensitivity::AsciiCaseInsensitive,
179 )
180 }
181
182 #[must_use]
184 pub fn with_attr_includes(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
185 self.push_attr(
186 name,
187 Some(AttributeOperator::Includes),
188 value.as_ref(),
189 CaseSensitivity::CaseSensitive,
190 )
191 }
192
193 #[must_use]
195 pub fn with_attr_dash_match(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
196 self.push_attr(
197 name,
198 Some(AttributeOperator::DashMatch),
199 value.as_ref(),
200 CaseSensitivity::CaseSensitive,
201 )
202 }
203
204 #[must_use]
206 pub fn with_attr_prefix(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
207 self.push_attr(
208 name,
209 Some(AttributeOperator::Prefix),
210 value.as_ref(),
211 CaseSensitivity::CaseSensitive,
212 )
213 }
214
215 #[must_use]
217 pub fn with_attr_suffix(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
218 self.push_attr(
219 name,
220 Some(AttributeOperator::Suffix),
221 value.as_ref(),
222 CaseSensitivity::CaseSensitive,
223 )
224 }
225
226 #[must_use]
228 pub fn with_attr_substring(self, name: impl AsRef<str>, value: impl AsRef<str>) -> Self {
229 self.push_attr(
230 name,
231 Some(AttributeOperator::Substring),
232 value.as_ref(),
233 CaseSensitivity::CaseSensitive,
234 )
235 }
236
237 #[must_use]
239 pub fn with_nth_child(mut self, a: i32, b: i32) -> Self {
240 self.nth.push(Nth {
241 ty: NthType::Child,
242 a,
243 b,
244 });
245 self
246 }
247
248 #[must_use]
250 pub fn with_nth_of_type(mut self, a: i32, b: i32) -> Self {
251 self.nth.push(Nth {
252 ty: NthType::OfType,
253 a,
254 b,
255 });
256 self
257 }
258
259 #[must_use]
261 pub fn with_first_child(self) -> Self {
262 self.with_nth_child(0, 1)
263 }
264
265 #[must_use]
267 pub fn with_first_of_type(self) -> Self {
268 self.with_nth_of_type(0, 1)
269 }
270
271 #[must_use]
273 pub fn without(mut self, compound: Self) -> Self {
274 self.negations.push(compound);
275 self
276 }
277
278 fn push_attr(
279 mut self,
280 name: impl AsRef<str>,
281 operator: Option<AttributeOperator>,
282 value: &str,
283 case: CaseSensitivity,
284 ) -> Self {
285 let name = name.as_ref();
286 debug_assert_ident("attribute name", name);
287 self.attributes.push(AttributeSelector {
288 name: name.to_ascii_lowercase().into(),
289 operator,
290 value: value.into(),
291 case,
292 });
293 self
294 }
295}