Skip to main content

pagesift_html/
select.rs

1//! CSS 选择器:解析成从右往左排列的复合选择器链,匹配时沿父节点、前兄弟回溯,过程中不分配内存。
2//!
3//! 支持的语法:类型、`*`、`#id`、`.class`、属性(`[a]` `=` `~=` `|=` `^=` `$=` `*=`,带 `i`/`s` 标志)、
4//! 后代/子/相邻兄弟/通用兄弟组合器、选择器列表、`:not()` `:is()` `:where()` `:has()`、
5//! `:nth-child()` `:nth-last-child()`(可带 `of S`)`:nth-of-type()` `:nth-last-of-type()`、
6//! `:first-child` `:last-child` `:only-child` `:first-of-type` `:last-of-type` `:only-of-type`、
7//! `:empty` `:root` `:scope`。
8//! 命名空间、伪元素和依赖浏览器状态的伪类(`:hover` 等)不支持,解析时报错。
9//!
10//! 语义和 scraper(Servo 的 selectors)一致:class 和 id 区分大小写(no-quirks),
11//! HTML 规范列出的一些属性(`type`、`lang` 等)的值默认不区分大小写。
12//!
13//! ```
14//! use pagesift_html::{Html, Selector};
15//!
16//! let doc = Html::parse(r#"<ul><li class="a">x</li><li class="b">y</li></ul>"#);
17//! let sel = Selector::parse("ul > li:not(.a)").unwrap();
18//! let hits: Vec<String> = doc.select(&sel).map(|n| n.text()).collect();
19//! assert_eq!(hits, ["y"]);
20//! ```
21
22use std::fmt;
23use std::str::FromStr;
24
25use crate::{Descendants, Element, Html, NodeId, NodeRef, Tag};
26
27/// 函数式伪类(`:not(` 等)的最大嵌套层数,限制解析器的递归深度。
28const MAX_NESTING: u32 = 32;
29/// 匹配时最深的递归层数(复合选择器个数加上嵌套),限制匹配器的栈深度。
30const MAX_DEPTH: u32 = 256;
31
32/// 解析好的选择器列表(逗号分隔)。解析一次,可以在多个文档、多个线程上复用。
33#[derive(Debug, Clone)]
34pub struct Selector {
35    list: Box<[Complex]>,
36}
37
38/// 复杂选择器,例如 `ul.nav > li a`。`parts[0]` 是最右边的复合选择器(匹配主体),
39/// `parts[i].comb` 是 `parts[i]` 和它左边的 `parts[i + 1]` 之间的组合器。
40#[derive(Debug, Clone)]
41struct Complex {
42    parts: Box<[Compound]>,
43}
44
45#[derive(Debug, Clone)]
46struct Compound {
47    /// 含已知标签的类型选择器时就是那个标签:匹配前先比节点的标签,多数节点不用构造 [`Element`]。
48    tag: Option<Tag>,
49    simples: Box<[Simple]>,
50    comb: Comb,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54enum Comb {
55    Descendant,
56    Child,
57    Next,
58    Later,
59}
60
61#[derive(Debug, Clone)]
62enum Simple {
63    /// 类型选择器。已知标签比枚举;`Other` 和外部内容(SVG 等)里的元素比小写名字。
64    Type(Tag, Box<str>),
65    Id(Box<str>),
66    Class(Box<str>),
67    Attr(Box<AttrSel>),
68    Nth(Box<Nth>),
69    Empty,
70    Root,
71    Scope,
72    /// `:has()` 里相对选择器的锚点,即正在判断 `:has()` 的那个元素。
73    Anchor,
74    Not(Selector),
75    Is(Selector),
76    Has(Selector),
77}
78
79impl Simple {
80    /// 复合选择器里先检查便宜的条件。
81    fn cost(&self) -> u8 {
82        match self {
83            Simple::Type(..) => 0,
84            Simple::Id(_) => 1,
85            Simple::Class(_) => 2,
86            Simple::Attr(_) => 3,
87            Simple::Empty | Simple::Root | Simple::Scope | Simple::Anchor => 4,
88            Simple::Nth(_) => 5,
89            Simple::Not(_) | Simple::Is(_) => 6,
90            Simple::Has(_) => 7,
91        }
92    }
93}
94
95#[derive(Debug, Clone)]
96struct AttrSel {
97    name: Box<str>,
98    op: AttrOp,
99    value: Box<str>,
100    /// 值按 ASCII 不区分大小写比较。
101    ci: bool,
102}
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105enum AttrOp {
106    Exists,
107    Equals,
108    Includes,
109    Dash,
110    Prefix,
111    Suffix,
112    Substring,
113    /// 永远不匹配:`~=` 的值为空或含空白,`^=` `$=` `*=` 的值为空。
114    Never,
115}
116
117/// `:nth-*()`:位置满足 `a*n + b`(n ≥ 0)。
118#[derive(Debug, Clone)]
119struct Nth {
120    a: i32,
121    b: i32,
122    /// 从后往前数。
123    last: bool,
124    /// 只数同类型的兄弟。
125    of_type: bool,
126    /// `of S`:只数匹配 S 的兄弟,自身也要匹配 S。
127    of: Option<Selector>,
128}
129
130impl Nth {
131    fn simple(a: i32, b: i32, last: bool, of_type: bool) -> Simple {
132        Simple::Nth(Box::new(Nth {
133            a,
134            b,
135            last,
136            of_type,
137            of: None,
138        }))
139    }
140}
141
142/// 选择器语法错误:出错位置(字节偏移)和原因。
143#[derive(Debug, Clone, PartialEq, Eq)]
144pub struct SelectorError {
145    offset: usize,
146    message: String,
147}
148
149impl SelectorError {
150    /// 出错位置在选择器字符串里的字节偏移。
151    pub fn offset(&self) -> usize {
152        self.offset
153    }
154
155    pub fn message(&self) -> &str {
156        &self.message
157    }
158}
159
160impl fmt::Display for SelectorError {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        write!(f, "{} at offset {}", self.message, self.offset)
163    }
164}
165
166impl std::error::Error for SelectorError {}
167
168impl Selector {
169    /// 解析选择器列表。语法错误、不支持的语法返回错误,不会 panic。
170    pub fn parse(s: &str) -> Result<Selector, SelectorError> {
171        let mut p = Parser {
172            s,
173            b: s.as_bytes(),
174            pos: 0,
175            nesting: 0,
176        };
177        let (sel, depth) = p.list(false, false)?;
178        if p.pos < p.b.len() {
179            return Err(p.unexpected());
180        }
181        if depth > MAX_DEPTH {
182            return Err(SelectorError {
183                offset: 0,
184                message: format!("selector is too complex (matching depth {depth} > {MAX_DEPTH})"),
185            });
186        }
187        Ok(sel)
188    }
189
190    /// 元素是否匹配。`:scope` 匹配根元素。不是元素的节点永远不匹配。
191    pub fn matches(&self, node: NodeRef<'_>) -> bool {
192        self.matches_with_scope(node, None)
193    }
194
195    /// 元素是否匹配,`:scope` 匹配 `scope`(为 `None` 时匹配根元素)。
196    pub fn matches_with_scope(&self, node: NodeRef<'_>, scope: Option<NodeRef<'_>>) -> bool {
197        let cx = Ctx {
198            scope: scope.filter(NodeRef::is_element).map(|s| s.id()),
199            anchor: None,
200        };
201        node.is_element() && self.matches_in(node, &cx)
202    }
203
204    #[inline]
205    fn matches_in(&self, el: NodeRef<'_>, cx: &Ctx) -> bool {
206        self.list.iter().any(|c| {
207            c.parts[0].tag_may_match(el) && match_complex(&c.parts, 0, el, cx) == Res::Matched
208        })
209    }
210}
211
212impl FromStr for Selector {
213    type Err = SelectorError;
214    fn from_str(s: &str) -> Result<Self, Self::Err> {
215        Selector::parse(s)
216    }
217}
218
219impl<'a> Html<'a> {
220    /// 文档里匹配选择器的所有元素,按文档顺序。
221    pub fn select<'s>(&'a self, sel: &'s Selector) -> Select<'a, 's> {
222        Select {
223            inner: self.root().descendants(),
224            sel,
225            cx: Ctx {
226                scope: None,
227                anchor: None,
228            },
229        }
230    }
231}
232
233impl<'a> NodeRef<'a> {
234    /// 后代(不含自身)里匹配选择器的元素,按文档顺序;`:scope` 匹配自身。
235    pub fn select<'s>(&self, sel: &'s Selector) -> Select<'a, 's> {
236        let mut inner = self.descendants();
237        inner.next();
238        Select {
239            inner,
240            sel,
241            cx: Ctx {
242                scope: self.is_element().then(|| self.id()),
243                anchor: None,
244            },
245        }
246    }
247}
248
249/// [`Html::select`] 和 [`NodeRef::select`] 返回的迭代器。
250pub struct Select<'a, 's> {
251    inner: Descendants<'a>,
252    sel: &'s Selector,
253    cx: Ctx,
254}
255
256impl<'a> Iterator for Select<'a, '_> {
257    type Item = NodeRef<'a>;
258    fn next(&mut self) -> Option<NodeRef<'a>> {
259        self.inner
260            .by_ref()
261            .find(|n| n.is_element() && self.sel.matches_in(*n, &self.cx))
262    }
263}
264
265// ---------------------------------------------------------------------------
266// 匹配
267// ---------------------------------------------------------------------------
268
269struct Ctx {
270    /// `:scope` 指向的元素;`None` 时是根元素。
271    scope: Option<NodeId>,
272    /// `:has()` 正在判断的元素。
273    anchor: Option<NodeId>,
274}
275
276/// 复杂选择器的匹配结果。失败时告诉上层从哪里重新开始,避免后代组合器的指数回溯
277/// (和 Servo selectors 的 `SelectorMatchingResult` 同一套规则)。
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
279enum Res {
280    Matched,
281    RestartFromLaterSibling,
282    RestartFromDescendant,
283    NotMatchedGlobally,
284}
285
286fn match_complex(parts: &[Compound], i: usize, el: NodeRef<'_>, cx: &Ctx) -> Res {
287    let part = &parts[i];
288    if !part.tag_may_match(el) || !match_compound(&part.simples, el, cx) {
289        return Res::RestartFromLaterSibling;
290    }
291    if i + 1 == parts.len() {
292        return Res::Matched;
293    }
294    let comb = part.comb;
295    let not_found = match comb {
296        Comb::Next | Comb::Later => Res::RestartFromDescendant,
297        Comb::Child | Comb::Descendant => Res::NotMatchedGlobally,
298    };
299    let mut cur = el;
300    loop {
301        let next = match comb {
302            Comb::Child | Comb::Descendant => cur.parent().filter(NodeRef::is_element),
303            Comb::Next | Comb::Later => prev_element(cur),
304        };
305        let Some(next) = next else {
306            return not_found;
307        };
308        // 标签对不上时不必递归,结果就是复合选择器不匹配
309        let r = if parts[i + 1].tag_may_match(next) {
310            match_complex(parts, i + 1, next, cx)
311        } else {
312            Res::RestartFromLaterSibling
313        };
314        match (r, comb) {
315            (Res::Matched | Res::NotMatchedGlobally, _) | (_, Comb::Next) => return r,
316            (_, Comb::Child) => return Res::RestartFromDescendant,
317            (Res::RestartFromDescendant, Comb::Later) => return r,
318            _ => {}
319        }
320        cur = next;
321    }
322}
323
324impl Compound {
325    /// 按标签快速排除。外部内容里的元素都是 Other,交给 [`type_matches`] 比名字。
326    #[inline]
327    fn tag_may_match(&self, el: NodeRef<'_>) -> bool {
328        match self.tag {
329            Some(t) => matches!(el.tag(), Some(x) if x == t || x == Tag::Other),
330            None => true,
331        }
332    }
333}
334
335fn match_compound(simples: &[Simple], el: NodeRef<'_>, cx: &Ctx) -> bool {
336    let Some(e) = el.as_element() else {
337        return false;
338    };
339    simples.iter().all(|s| match_simple(s, el, &e, cx))
340}
341
342fn match_simple(s: &Simple, el: NodeRef<'_>, e: &Element<'_>, cx: &Ctx) -> bool {
343    match s {
344        Simple::Type(tag, name) => type_matches(e, *tag, name),
345        Simple::Id(id) => e.attr("id") == Some(&**id),
346        Simple::Class(c) => e
347            .attr("class")
348            .is_some_and(|v| v.split_ascii_whitespace().any(|x| x == &**c)),
349        Simple::Attr(a) => e.attr(&a.name).is_some_and(|v| attr_matches(a, v)),
350        Simple::Nth(n) => nth_matches(n, el, e, cx),
351        Simple::Empty => !el.children().any(|c| c.is_element() || c.is_text()),
352        Simple::Root => el.parent().is_some_and(|p| p.is_document()),
353        Simple::Scope => match cx.scope {
354            Some(id) => el.id() == id,
355            None => el.parent().is_some_and(|p| p.is_document()),
356        },
357        Simple::Anchor => cx.anchor == Some(el.id()),
358        Simple::Not(sel) => !sel.matches_in(el, cx),
359        Simple::Is(sel) => sel.matches_in(el, cx),
360        Simple::Has(sel) => has_matches(sel, el, cx),
361    }
362}
363
364fn type_matches(e: &Element<'_>, tag: Tag, name: &str) -> bool {
365    let t = e.tag();
366    if t == tag {
367        tag != Tag::Other || e.name() == name
368    } else {
369        // SVG/MathML 里的元素都是 Other,名字可能和 HTML 标签相同(例如 svg 的 <a>)
370        t == Tag::Other && e.name() == name
371    }
372}
373
374fn attr_matches(a: &AttrSel, v: &str) -> bool {
375    let (v, want) = (v.as_bytes(), a.value.as_bytes());
376    let eq = |x: &[u8], y: &[u8]| {
377        if a.ci {
378            x.eq_ignore_ascii_case(y)
379        } else {
380            x == y
381        }
382    };
383    match a.op {
384        AttrOp::Exists => true,
385        AttrOp::Equals => eq(v, want),
386        AttrOp::Includes => v
387            .split(|c| matches!(c, b' ' | b'\t' | b'\n' | b'\x0c' | b'\r'))
388            .any(|w| eq(w, want)),
389        AttrOp::Dash => {
390            eq(v, want)
391                || (v.len() > want.len() && v[want.len()] == b'-' && eq(&v[..want.len()], want))
392        }
393        AttrOp::Prefix => v.len() >= want.len() && eq(&v[..want.len()], want),
394        AttrOp::Suffix => v.len() >= want.len() && eq(&v[v.len() - want.len()..], want),
395        AttrOp::Substring => {
396            if a.ci {
397                v.windows(want.len()).any(|w| w.eq_ignore_ascii_case(want))
398            } else {
399                memchr::memmem::find(v, want).is_some()
400            }
401        }
402        AttrOp::Never => false,
403    }
404}
405
406fn nth_matches(n: &Nth, el: NodeRef<'_>, e: &Element<'_>, cx: &Ctx) -> bool {
407    if let Some(of) = &n.of
408        && !of.matches_in(el, cx)
409    {
410        return false;
411    }
412    let tag = e.tag();
413    let counts = |s: NodeRef<'_>| {
414        let Some(st) = s.tag() else {
415            return false;
416        };
417        if n.of_type {
418            st == tag
419                && (tag != Tag::Other || s.as_element().is_some_and(|se| se.name() == e.name()))
420        } else if let Some(of) = &n.of {
421            of.matches_in(s, cx)
422        } else {
423            true
424        }
425    };
426    let mut pos = 1i64;
427    let mut cur = el;
428    loop {
429        let next = if n.last {
430            cur.next_sibling()
431        } else {
432            cur.prev_sibling()
433        };
434        let Some(s) = next else { break };
435        if counts(s) {
436            pos += 1;
437        }
438        cur = s;
439    }
440    let (a, b) = (i64::from(n.a), i64::from(n.b));
441    if a == 0 {
442        pos == b
443    } else {
444        let d = pos - b;
445        d % a == 0 && d / a >= 0
446    }
447}
448
449/// `:has()`:锚点后面(后代或之后的兄弟及其后代)有元素匹配相对选择器。
450fn has_matches(sel: &Selector, el: NodeRef<'_>, cx: &Ctx) -> bool {
451    let inner = Ctx {
452        scope: cx.scope,
453        anchor: Some(el.id()),
454    };
455    sel.list.iter().any(|c| {
456        let leading = c.parts[c.parts.len() - 2].comb;
457        let hit = |n: NodeRef<'_>| {
458            n.is_element() && match_complex(&c.parts, 0, n, &inner) == Res::Matched
459        };
460        // 只有一个复合选择器时(`:has(> img)`、`:has(+ p)`),候选范围可以收窄到子节点或兄弟
461        let single = c.parts.len() == 2;
462        match leading {
463            Comb::Child if single => el.children().any(hit),
464            Comb::Next if single => next_element(el).is_some_and(hit),
465            Comb::Later if single => {
466                let mut s = el.next_sibling();
467                while let Some(n) = s {
468                    if hit(n) {
469                        return true;
470                    }
471                    s = n.next_sibling();
472                }
473                false
474            }
475            Comb::Descendant | Comb::Child => el.descendants().skip(1).any(hit),
476            Comb::Next | Comb::Later => {
477                let mut s = el.next_sibling();
478                while let Some(n) = s {
479                    if n.descendants().any(hit) {
480                        return true;
481                    }
482                    s = n.next_sibling();
483                }
484                false
485            }
486        }
487    })
488}
489
490fn next_element<'a>(n: NodeRef<'a>) -> Option<NodeRef<'a>> {
491    let mut cur = n.next_sibling();
492    while let Some(c) = cur {
493        if c.is_element() {
494            return Some(c);
495        }
496        cur = c.next_sibling();
497    }
498    None
499}
500
501fn prev_element<'a>(n: NodeRef<'a>) -> Option<NodeRef<'a>> {
502    let mut cur = n.prev_sibling();
503    while let Some(c) = cur {
504        if c.is_element() {
505            return Some(c);
506        }
507        cur = c.prev_sibling();
508    }
509    None
510}
511
512// ---------------------------------------------------------------------------
513// 解析
514// ---------------------------------------------------------------------------
515
516/// HTML 规范里值不区分大小写的属性(selectors 对 HTML 元素也按这张表处理)。
517const CASE_INSENSITIVE_ATTRS: &[&str] = &[
518    "accept",
519    "accept-charset",
520    "align",
521    "alink",
522    "axis",
523    "bgcolor",
524    "charset",
525    "checked",
526    "clear",
527    "codetype",
528    "color",
529    "compact",
530    "declare",
531    "defer",
532    "dir",
533    "direction",
534    "disabled",
535    "enctype",
536    "face",
537    "frame",
538    "hreflang",
539    "http-equiv",
540    "lang",
541    "language",
542    "link",
543    "media",
544    "method",
545    "multiple",
546    "nohref",
547    "noresize",
548    "noshade",
549    "nowrap",
550    "readonly",
551    "rel",
552    "rev",
553    "rules",
554    "scope",
555    "scrolling",
556    "selected",
557    "shape",
558    "target",
559    "text",
560    "type",
561    "valign",
562    "valuetype",
563    "vlink",
564];
565
566struct Parser<'a> {
567    s: &'a str,
568    b: &'a [u8],
569    pos: usize,
570    nesting: u32,
571}
572
573fn is_ws(c: u8) -> bool {
574    matches!(c, b' ' | b'\t' | b'\n' | b'\r' | b'\x0c')
575}
576
577fn is_name_start(c: u8) -> bool {
578    c.is_ascii_alphabetic() || c == b'_' || c >= 0x80
579}
580
581fn is_name(c: u8) -> bool {
582    is_name_start(c) || c.is_ascii_digit() || c == b'-'
583}
584
585impl Parser<'_> {
586    fn peek(&self) -> Option<u8> {
587        self.b.get(self.pos).copied()
588    }
589
590    fn peek_at(&self, i: usize) -> Option<u8> {
591        self.b.get(self.pos + i).copied()
592    }
593
594    fn err(&self, message: impl Into<String>) -> SelectorError {
595        SelectorError {
596            offset: self.pos,
597            message: message.into(),
598        }
599    }
600
601    fn unexpected(&self) -> SelectorError {
602        match self.s[self.pos..].chars().next() {
603            Some(c) => self.err(format!("unexpected `{c}`")),
604            None => self.err("unexpected end of selector"),
605        }
606    }
607
608    /// 跳过空白和注释,返回是否有空白(注释本身不算空白)。
609    fn skip_ws(&mut self) -> bool {
610        let mut ws = false;
611        loop {
612            match self.peek() {
613                Some(c) if is_ws(c) => {
614                    ws = true;
615                    self.pos += 1;
616                }
617                Some(b'/') if self.peek_at(1) == Some(b'*') => {
618                    self.pos = match memchr::memmem::find(&self.b[self.pos + 2..], b"*/") {
619                        Some(i) => self.pos + 2 + i + 2,
620                        None => self.b.len(),
621                    };
622                }
623                _ => return ws,
624            }
625        }
626    }
627
628    /// 选择器列表,返回列表和匹配深度。`nested` 时以 `)` 结束(不消耗)。
629    fn list(&mut self, relative: bool, nested: bool) -> Result<(Selector, u32), SelectorError> {
630        let mut list = Vec::new();
631        let mut depth = 0;
632        loop {
633            self.skip_ws();
634            let (c, d) = self.complex(relative)?;
635            list.push(c);
636            depth = depth.max(d);
637            self.skip_ws();
638            match self.peek() {
639                Some(b',') => self.pos += 1,
640                Some(b')') if nested => break,
641                None if !nested => break,
642                None => return Err(self.err("expected `)`")),
643                _ => return Err(self.unexpected()),
644            }
645        }
646        Ok((
647            Selector {
648                list: list.into_boxed_slice(),
649            },
650            depth,
651        ))
652    }
653
654    fn combinator(&mut self) -> Option<Comb> {
655        let c = match self.peek()? {
656            b'>' => Comb::Child,
657            b'+' => Comb::Next,
658            b'~' => Comb::Later,
659            _ => return None,
660        };
661        self.pos += 1;
662        Some(c)
663    }
664
665    /// 复杂选择器。`relative` 时可以以组合器开头(`:has(> a)`),左端补一个锚点。
666    fn complex(&mut self, relative: bool) -> Result<(Complex, u32), SelectorError> {
667        // 从左往右收集,最后反转成从右往左
668        let mut parts: Vec<(Vec<Simple>, u32)> = Vec::new();
669        let mut combs: Vec<Comb> = Vec::new();
670        if relative {
671            let comb = self.combinator().unwrap_or(Comb::Descendant);
672            self.skip_ws();
673            parts.push((vec![Simple::Anchor], 0));
674            combs.push(comb);
675        }
676        parts.push(self.compound()?);
677        loop {
678            let ws = self.skip_ws();
679            let comb = match self.combinator() {
680                Some(c) => {
681                    self.skip_ws();
682                    c
683                }
684                None => match self.peek() {
685                    None | Some(b',' | b')') => break,
686                    Some(_) if ws => Comb::Descendant,
687                    Some(_) => return Err(self.unexpected()),
688                },
689            };
690            combs.push(comb);
691            parts.push(self.compound()?);
692        }
693        let depth = parts.iter().map(|(_, d)| 1 + d).sum();
694        let n = parts.len();
695        let parts = parts
696            .into_iter()
697            .rev()
698            .enumerate()
699            .map(|(i, (mut simples, _))| {
700                simples.sort_by_key(Simple::cost);
701                let tag = simples.iter().find_map(|s| match s {
702                    Simple::Type(t, _) if *t != Tag::Other => Some(*t),
703                    _ => None,
704                });
705                Compound {
706                    tag,
707                    simples: simples.into_boxed_slice(),
708                    // 最左边的复合选择器没有左邻,组合器不会被用到
709                    comb: if i + 1 < n {
710                        combs[n - 2 - i]
711                    } else {
712                        Comb::Descendant
713                    },
714                }
715            })
716            .collect();
717        Ok((Complex { parts }, depth))
718    }
719
720    /// 复合选择器,返回简单选择器和其中嵌套选择器的最大匹配深度。
721    fn compound(&mut self) -> Result<(Vec<Simple>, u32), SelectorError> {
722        let mut simples = Vec::new();
723        let mut depth = 0;
724        let mut any = false;
725        match self.peek() {
726            Some(b'*') => {
727                self.pos += 1;
728                any = true;
729            }
730            _ if self.ident_start() => {
731                let name = self.ident()?.to_ascii_lowercase();
732                simples.push(Simple::Type(Tag::from_name(&name), name.into()));
733                any = true;
734            }
735            _ => {}
736        }
737        if self.peek() == Some(b'|') {
738            return Err(self.err("namespaces are not supported"));
739        }
740        loop {
741            match self.peek() {
742                Some(b'#') => {
743                    self.pos += 1;
744                    if !self.ident_start() {
745                        return Err(self.err("expected an identifier after `#`"));
746                    }
747                    simples.push(Simple::Id(self.ident()?.into()));
748                }
749                Some(b'.') => {
750                    self.pos += 1;
751                    if !self.ident_start() {
752                        return Err(self.err("expected an identifier after `.`"));
753                    }
754                    simples.push(Simple::Class(self.ident()?.into()));
755                }
756                Some(b'[') => {
757                    self.pos += 1;
758                    simples.push(self.attr()?);
759                }
760                Some(b':') => {
761                    self.pos += 1;
762                    let (s, d) = self.pseudo()?;
763                    depth = depth.max(d);
764                    simples.extend(s);
765                }
766                _ => break,
767            }
768            any = true;
769        }
770        if !any {
771            return Err(match self.peek() {
772                None => self.err("expected a selector"),
773                Some(_) => self.unexpected(),
774            });
775        }
776        Ok((simples, depth))
777    }
778
779    fn attr(&mut self) -> Result<Simple, SelectorError> {
780        self.skip_ws();
781        if matches!(self.peek(), Some(b'*' | b'|')) {
782            return Err(self.err("namespaces are not supported"));
783        }
784        if !self.ident_start() {
785            return Err(self.err("expected an attribute name"));
786        }
787        let name = self.ident()?.to_ascii_lowercase();
788        if self.peek() == Some(b'|') && self.peek_at(1) != Some(b'=') {
789            return Err(self.err("namespaces are not supported"));
790        }
791        self.skip_ws();
792        let op = match (self.peek(), self.peek_at(1)) {
793            (Some(b']'), _) => {
794                self.pos += 1;
795                return Ok(Simple::Attr(Box::new(AttrSel {
796                    name: name.into(),
797                    op: AttrOp::Exists,
798                    value: "".into(),
799                    ci: false,
800                })));
801            }
802            (Some(b'='), _) => AttrOp::Equals,
803            (Some(b'~'), Some(b'=')) => AttrOp::Includes,
804            (Some(b'|'), Some(b'=')) => AttrOp::Dash,
805            (Some(b'^'), Some(b'=')) => AttrOp::Prefix,
806            (Some(b'$'), Some(b'=')) => AttrOp::Suffix,
807            (Some(b'*'), Some(b'=')) => AttrOp::Substring,
808            (None, _) => return Err(self.err("unclosed attribute selector, expected `]`")),
809            _ => return Err(self.unexpected()),
810        };
811        self.pos += if op == AttrOp::Equals { 1 } else { 2 };
812        self.skip_ws();
813        let value = match self.peek() {
814            Some(q @ (b'"' | b'\'')) => self.string(q)?,
815            _ if self.ident_start() => self.ident()?,
816            None => return Err(self.err("expected an attribute value")),
817            _ => {
818                return Err(
819                    self.err("expected an identifier or a quoted string as the attribute value")
820                );
821            }
822        };
823        self.skip_ws();
824        let mut ci = CASE_INSENSITIVE_ATTRS.contains(&name.as_str());
825        if self.ident_start() {
826            let flag_at = self.pos;
827            let flag = self.ident()?;
828            if flag.eq_ignore_ascii_case("i") {
829                ci = true;
830            } else if flag.eq_ignore_ascii_case("s") {
831                ci = false;
832            } else {
833                self.pos = flag_at;
834                return Err(self.err(format!("unknown attribute selector flag `{flag}`")));
835            }
836            self.skip_ws();
837        }
838        match self.peek() {
839            Some(b']') => self.pos += 1,
840            None => return Err(self.err("unclosed attribute selector, expected `]`")),
841            _ => return Err(self.unexpected()),
842        }
843        let op = match op {
844            AttrOp::Includes if value.is_empty() || value.bytes().any(is_ws) => AttrOp::Never,
845            AttrOp::Prefix | AttrOp::Suffix | AttrOp::Substring if value.is_empty() => {
846                AttrOp::Never
847            }
848            op => op,
849        };
850        Ok(Simple::Attr(Box::new(AttrSel {
851            name: name.into(),
852            op,
853            value: value.into(),
854            ci,
855        })))
856    }
857
858    /// `:` 之后的伪类。`:only-child` 这类展开成两个简单选择器。
859    fn pseudo(&mut self) -> Result<(Vec<Simple>, u32), SelectorError> {
860        if self.peek() == Some(b':') {
861            return Err(self.err("pseudo-elements are not supported"));
862        }
863        let start = self.pos;
864        if !self.ident_start() {
865            return Err(self.err("expected a pseudo-class name after `:`"));
866        }
867        let name = self.ident()?.to_ascii_lowercase();
868        if self.peek() == Some(b'(') {
869            self.pos += 1;
870            return self.pseudo_fn(&name, start);
871        }
872        let s = match name.as_str() {
873            "first-child" => vec![Nth::simple(0, 1, false, false)],
874            "last-child" => vec![Nth::simple(0, 1, true, false)],
875            "only-child" => vec![
876                Nth::simple(0, 1, false, false),
877                Nth::simple(0, 1, true, false),
878            ],
879            "first-of-type" => vec![Nth::simple(0, 1, false, true)],
880            "last-of-type" => vec![Nth::simple(0, 1, true, true)],
881            "only-of-type" => vec![
882                Nth::simple(0, 1, false, true),
883                Nth::simple(0, 1, true, true),
884            ],
885            "empty" => vec![Simple::Empty],
886            "root" => vec![Simple::Root],
887            "scope" => vec![Simple::Scope],
888            _ => {
889                self.pos = start;
890                return Err(self.err(format!("unsupported pseudo-class `:{name}`")));
891            }
892        };
893        Ok((s, 0))
894    }
895
896    fn pseudo_fn(&mut self, name: &str, start: usize) -> Result<(Vec<Simple>, u32), SelectorError> {
897        let nested = |p: &mut Self, relative: bool| -> Result<(Selector, u32), SelectorError> {
898            if p.nesting >= MAX_NESTING {
899                return Err(p.err(format!("selector nested deeper than {MAX_NESTING} levels")));
900            }
901            p.nesting += 1;
902            let r = p.list(relative, true);
903            p.nesting -= 1;
904            r
905        };
906        let (simple, depth) = match name {
907            "not" => {
908                let (s, d) = nested(self, false)?;
909                (Simple::Not(s), d)
910            }
911            "is" | "where" | "matches" | "any" => {
912                let (s, d) = nested(self, false)?;
913                (Simple::Is(s), d)
914            }
915            "has" => {
916                let (s, d) = nested(self, true)?;
917                (Simple::Has(s), d)
918            }
919            "nth-child" | "nth-last-child" | "nth-of-type" | "nth-last-of-type" => {
920                self.skip_ws();
921                let (a, b) = self.an_plus_b()?;
922                let ws = self.skip_ws();
923                let mut nth = Nth {
924                    a,
925                    b,
926                    last: name.starts_with("nth-last"),
927                    of_type: name.ends_with("of-type"),
928                    of: None,
929                };
930                let mut depth = 0;
931                if !nth.of_type && ws && self.ident_start() {
932                    let at = self.pos;
933                    if self.ident()?.eq_ignore_ascii_case("of") && self.skip_ws() {
934                        let (s, d) = nested(self, false)?;
935                        nth.of = Some(s);
936                        depth = d;
937                    } else {
938                        self.pos = at;
939                        return Err(self.unexpected());
940                    }
941                }
942                (Simple::Nth(Box::new(nth)), depth)
943            }
944            _ => {
945                self.pos = start;
946                return Err(self.err(format!("unsupported pseudo-class `:{name}()`")));
947            }
948        };
949        self.skip_ws();
950        match self.peek() {
951            Some(b')') => self.pos += 1,
952            None => return Err(self.err(format!("unclosed `:{name}(`, expected `)`"))),
953            _ => return Err(self.unexpected()),
954        }
955        Ok((vec![simple], depth))
956    }
957
958    /// `An+B` 记法:`odd`、`even`、`3`、`-n+2`、`2n + 1` 等。
959    fn an_plus_b(&mut self) -> Result<(i32, i32), SelectorError> {
960        let start = self.pos;
961        let bad = |p: &mut Self| {
962            p.pos = start;
963            Err(p.err("invalid An+B expression"))
964        };
965        if self.ident_start() {
966            let at = self.pos;
967            let word = self.ident()?;
968            if word.eq_ignore_ascii_case("odd") {
969                return Ok((2, 1));
970            }
971            if word.eq_ignore_ascii_case("even") {
972                return Ok((2, 0));
973            }
974            self.pos = at;
975        }
976        let sign = match self.peek() {
977            Some(b'+') => {
978                self.pos += 1;
979                1
980            }
981            Some(b'-') => {
982                self.pos += 1;
983                -1
984            }
985            _ => 1,
986        };
987        let digits = self.digits();
988        if matches!(self.peek(), Some(b'n' | b'N')) {
989            self.pos += 1;
990            let a = sign * digits.unwrap_or(1);
991            // n 后面紧跟的字符不能再组成标识符(例如 `2nd`)
992            if self.peek().is_some_and(|c| is_name(c) && c != b'-') {
993                return bad(self);
994            }
995            let save = self.pos;
996            self.skip_ws();
997            let bsign = match self.peek() {
998                Some(b'+') => 1,
999                Some(b'-') => -1,
1000                _ => {
1001                    self.pos = save;
1002                    return Ok((a, 0));
1003                }
1004            };
1005            self.pos += 1;
1006            self.skip_ws();
1007            match self.digits() {
1008                Some(b) => Ok((a, bsign * b)),
1009                None => bad(self),
1010            }
1011        } else {
1012            match digits {
1013                Some(b) => Ok((0, sign * b)),
1014                None => bad(self),
1015            }
1016        }
1017    }
1018
1019    fn digits(&mut self) -> Option<i32> {
1020        let start = self.pos;
1021        let mut v: i32 = 0;
1022        while let Some(c @ b'0'..=b'9') = self.peek() {
1023            v = v.saturating_mul(10).saturating_add(i32::from(c - b'0'));
1024            self.pos += 1;
1025        }
1026        (self.pos > start).then_some(v)
1027    }
1028
1029    /// 当前位置能否开始一个标识符(CSS 语法的 "would start an identifier")。
1030    fn ident_start(&self) -> bool {
1031        let valid_escape = |i: usize| {
1032            self.peek_at(i) == Some(b'\\')
1033                && !matches!(self.peek_at(i + 1), Some(b'\n' | b'\r' | b'\x0c'))
1034        };
1035        match self.peek() {
1036            Some(b'-') => {
1037                matches!(self.peek_at(1), Some(c) if is_name_start(c) || c == b'-')
1038                    || valid_escape(1)
1039            }
1040            Some(c) if is_name_start(c) => true,
1041            _ => valid_escape(0),
1042        }
1043    }
1044
1045    fn ident(&mut self) -> Result<String, SelectorError> {
1046        let mut out = String::new();
1047        loop {
1048            match self.peek() {
1049                Some(b'\\') if !matches!(self.peek_at(1), Some(b'\n' | b'\r' | b'\x0c')) => {
1050                    self.pos += 1;
1051                    out.push(self.escape());
1052                }
1053                Some(c) if is_name(c) => {
1054                    let ch = self.s[self.pos..].chars().next().unwrap_or('\u{fffd}');
1055                    out.push(ch);
1056                    self.pos += ch.len_utf8();
1057                }
1058                _ => break,
1059            }
1060        }
1061        if out.is_empty() {
1062            return Err(self.err("expected an identifier"));
1063        }
1064        Ok(out)
1065    }
1066
1067    /// 反斜杠之后的转义:1~6 位十六进制(后面可跟一个空白)或任意一个字符。
1068    fn escape(&mut self) -> char {
1069        let start = self.pos;
1070        let mut v: u32 = 0;
1071        while self.pos - start < 6
1072            && let Some(c) = self.peek()
1073            && c.is_ascii_hexdigit()
1074        {
1075            v = v * 16 + (c as char).to_digit(16).unwrap_or(0);
1076            self.pos += 1;
1077        }
1078        if self.pos > start {
1079            if self.peek() == Some(b'\r') && self.peek_at(1) == Some(b'\n') {
1080                self.pos += 2;
1081            } else if self.peek().is_some_and(is_ws) {
1082                self.pos += 1;
1083            }
1084            return match char::from_u32(v) {
1085                Some(c) if v != 0 => c,
1086                _ => '\u{fffd}',
1087            };
1088        }
1089        match self.s[self.pos..].chars().next() {
1090            Some(c) => {
1091                self.pos += c.len_utf8();
1092                c
1093            }
1094            None => '\u{fffd}',
1095        }
1096    }
1097
1098    /// 引号字符串,`q` 是引号字符。到结尾没闭合也接受(和 CSS 语法一致)。
1099    fn string(&mut self, q: u8) -> Result<String, SelectorError> {
1100        self.pos += 1;
1101        let mut out = String::new();
1102        loop {
1103            match self.peek() {
1104                None => return Ok(out),
1105                Some(c) if c == q => {
1106                    self.pos += 1;
1107                    return Ok(out);
1108                }
1109                Some(b'\n' | b'\r' | b'\x0c') => {
1110                    return Err(self.err("unterminated string"));
1111                }
1112                Some(b'\\') => {
1113                    self.pos += 1;
1114                    match self.peek() {
1115                        None => {}
1116                        Some(b'\r') if self.peek_at(1) == Some(b'\n') => self.pos += 2,
1117                        Some(b'\n' | b'\r' | b'\x0c') => self.pos += 1,
1118                        Some(_) => out.push(self.escape()),
1119                    }
1120                }
1121                Some(_) => {
1122                    let ch = self.s[self.pos..].chars().next().unwrap_or('\u{fffd}');
1123                    out.push(ch);
1124                    self.pos += ch.len_utf8();
1125                }
1126            }
1127        }
1128    }
1129}
1130
1131#[cfg(test)]
1132mod tests {
1133    use super::*;
1134
1135    const DOC: &str = r##"<!doctype html><html lang="EN"><head><title>T</title></head><body>
1136        <div id="main" class="wrap  big">
1137          <h1 class="title">Head</h1>
1138          <p class="lead intro" data-x="foo-bar">One</p>
1139          <p>Two <a href="https://a.example/x.pdf" rel="NoFollow">A</a></p>
1140          <p></p>
1141          <!-- c -->
1142          <ul><li>1</li><li class="odd">2</li><li>3</li><li>4</li><li>5</li></ul>
1143          <span lang="en-US">S</span><em>E</em><span>S2</span>
1144          <svg><a href="#s">svg-a</a></svg>
1145          <my-widget data-k="Ü">W</my-widget>
1146        </div>
1147        <table><tr><td>c1</td><td>c2</td></tr></table>
1148    </body></html>"##;
1149
1150    fn sel(doc: &Html<'_>, s: &str) -> Vec<String> {
1151        let sel = Selector::parse(s).unwrap_or_else(|e| panic!("{s}: {e}"));
1152        doc.select(&sel)
1153            .map(|n| {
1154                let e = n.as_element().unwrap();
1155                let t = n.text();
1156                if t.trim().is_empty() {
1157                    e.name().to_owned()
1158                } else {
1159                    t.trim().to_owned()
1160                }
1161            })
1162            .collect()
1163    }
1164
1165    #[test]
1166    fn simple_selectors() {
1167        let doc = Html::parse(DOC);
1168        assert_eq!(sel(&doc, "h1"), ["Head"]);
1169        assert_eq!(sel(&doc, "H1"), ["Head"]);
1170        assert_eq!(sel(&doc, "#main > .title"), ["Head"]);
1171        assert_eq!(sel(&doc, "p.lead.intro"), ["One"]);
1172        assert_eq!(sel(&doc, ".wrap.big > h1"), ["Head"]);
1173        assert!(sel(&doc, ".WRAP").is_empty());
1174        assert_eq!(sel(&doc, "my-widget"), ["W"]);
1175        assert_eq!(sel(&doc, "svg a"), ["svg-a"]);
1176        assert_eq!(sel(&doc, "a").len(), 2);
1177        assert_eq!(sel(&doc, "td:last-child"), ["c2"]);
1178        assert_eq!(
1179            sel(&doc, "*").len(),
1180            doc.root().descendants().filter(|n| n.is_element()).count()
1181        );
1182        assert_eq!(sel(&doc, "#m\\61 in > h1"), ["Head"]);
1183        assert_eq!(sel(&doc, "h1, li.odd, h1"), ["Head", "2"]);
1184    }
1185
1186    #[test]
1187    fn attribute_selectors() {
1188        let doc = Html::parse(DOC);
1189        assert_eq!(sel(&doc, "[data-x]"), ["One"]);
1190        assert_eq!(sel(&doc, "[data-x=foo-bar]"), ["One"]);
1191        assert_eq!(sel(&doc, "[data-x='foo-bar']"), ["One"]);
1192        assert_eq!(sel(&doc, "[data-x|=foo]"), ["One"]);
1193        assert!(sel(&doc, "[data-x|=fo]").is_empty());
1194        assert_eq!(sel(&doc, "[data-x^=foo]"), ["One"]);
1195        assert_eq!(sel(&doc, "[data-x$=\"bar\"]"), ["One"]);
1196        assert_eq!(sel(&doc, "[data-x*='o-b']"), ["One"]);
1197        assert!(sel(&doc, "[data-x*='O-B']").is_empty());
1198        assert_eq!(sel(&doc, "[data-x*='O-B' i]"), ["One"]);
1199        assert_eq!(sel(&doc, "[class~=intro]"), ["One"]);
1200        assert!(sel(&doc, "[class~='']").is_empty());
1201        assert!(sel(&doc, "[data-x^='']").is_empty());
1202        assert_eq!(sel(&doc, "a[href$='.PDF' i]"), ["A"]);
1203        // rel、lang 属于 HTML 不区分大小写的属性
1204        assert_eq!(sel(&doc, "a[rel=nofollow]"), ["A"]);
1205        assert!(sel(&doc, "a[rel=nofollow s]").is_empty());
1206        assert_eq!(sel(&doc, "span[lang|=EN]"), ["S"]);
1207        assert_eq!(sel(&doc, "[data-k='Ü']"), ["W"]);
1208    }
1209
1210    #[test]
1211    fn combinators() {
1212        let doc = Html::parse(DOC);
1213        assert_eq!(sel(&doc, "body li"), ["1", "2", "3", "4", "5"]);
1214        assert!(sel(&doc, "body > li").is_empty());
1215        assert_eq!(sel(&doc, "h1 + p"), ["One"]);
1216        assert_eq!(sel(&doc, "h1 ~ p"), ["One", "Two A", "p"]);
1217        assert_eq!(sel(&doc, "li.odd ~ li"), ["3", "4", "5"]);
1218        assert_eq!(sel(&doc, "li.odd+li"), ["3"]);
1219        assert_eq!(sel(&doc, "span + em + span"), ["S2"]);
1220        assert_eq!(sel(&doc, "div p > a"), ["A"]);
1221        assert_eq!(sel(&doc, "html body div ul > li + li ~ li").len(), 3);
1222        assert_eq!(sel(&doc, "#main /* c */ h1"), ["Head"]);
1223    }
1224
1225    #[test]
1226    fn pseudo_classes() {
1227        let doc = Html::parse(DOC);
1228        assert_eq!(sel(&doc, "li:first-child"), ["1"]);
1229        assert_eq!(sel(&doc, "li:last-child"), ["5"]);
1230        assert_eq!(sel(&doc, "li:nth-child(2)"), ["2"]);
1231        assert_eq!(sel(&doc, "li:nth-child(odd)"), ["1", "3", "5"]);
1232        assert_eq!(sel(&doc, "li:nth-child(EVEN)"), ["2", "4"]);
1233        assert_eq!(sel(&doc, "li:nth-child(-n+2)"), ["1", "2"]);
1234        assert_eq!(sel(&doc, "li:nth-child(2n + 1)"), ["1", "3", "5"]);
1235        assert_eq!(sel(&doc, "li:nth-child(n+4)"), ["4", "5"]);
1236        assert_eq!(sel(&doc, "li:nth-last-child(2)"), ["4"]);
1237        assert_eq!(sel(&doc, "li:nth-child(odd of :not(.odd))"), ["1", "4"]);
1238        assert_eq!(sel(&doc, "#main > p:nth-of-type(2)"), ["Two A"]);
1239        assert_eq!(sel(&doc, "#main > span:last-of-type"), ["S2"]);
1240        assert_eq!(sel(&doc, "#main > :only-of-type").len(), 5);
1241        assert_eq!(sel(&doc, "title:only-child"), ["T"]);
1242        assert_eq!(sel(&doc, "#main > p:empty"), ["p"]);
1243        assert_eq!(sel(&doc, ":root").len(), 1);
1244        assert_eq!(sel(&doc, "p:not(.lead):not(:empty)"), ["Two A"]);
1245        assert_eq!(sel(&doc, "p:not(.lead, :empty)"), ["Two A"]);
1246        assert_eq!(sel(&doc, ":is(h1, li.odd)"), ["Head", "2"]);
1247        assert_eq!(sel(&doc, ":where(#main) > h1"), ["Head"]);
1248        assert_eq!(sel(&doc, "p:has(> a)"), ["Two A"]);
1249        assert_eq!(sel(&doc, "h1:has(+ p.lead)"), ["Head"]);
1250        assert_eq!(sel(&doc, "li:has(~ li.odd)"), ["1"]);
1251        assert_eq!(sel(&doc, "div:has(li.odd)").len(), 1);
1252        assert_eq!(sel(&doc, "body > :not(div, table)").len(), 0);
1253    }
1254
1255    #[test]
1256    fn scope() {
1257        let doc = Html::parse(DOC);
1258        let ul = doc.select(&"ul".parse().unwrap()).next().unwrap();
1259        let s: Selector = ":scope > li:first-child".parse().unwrap();
1260        assert_eq!(ul.select(&s).map(|n| n.text()).collect::<Vec<_>>(), ["1"]);
1261        // 后代不含自身
1262        let s: Selector = "ul".parse().unwrap();
1263        assert_eq!(ul.select(&s).count(), 0);
1264        let html = doc.select(&":scope".parse().unwrap()).collect::<Vec<_>>();
1265        assert_eq!(html.len(), 1);
1266        assert_eq!(html[0].tag(), Some(Tag::Html));
1267    }
1268
1269    #[test]
1270    fn errors() {
1271        for (s, msg) in [
1272            ("", "expected a selector"),
1273            ("div[", "attribute"),
1274            ("p:::x", "pseudo-elements"),
1275            ("p::before", "pseudo-elements"),
1276            ("a:hover", "unsupported pseudo-class `:hover`"),
1277            ("a:foo(1)", "unsupported pseudo-class `:foo()`"),
1278            ("a >", "expected a selector"),
1279            ("a,", "expected a selector"),
1280            (",a", "unexpected `,`"),
1281            ("a > > b", "unexpected `>`"),
1282            ("svg|a", "namespaces"),
1283            ("[x=1]", "attribute value"),
1284            ("[x=y z]", "unknown attribute selector flag"),
1285            ("li:nth-child(2nd)", "An+B"),
1286            ("li:nth-child(+ 2)", "An+B"),
1287            ("li:nth-child(2n+)", "An+B"),
1288            (":not(a", "expected `)`"),
1289            (":not()", "unexpected `)`"),
1290            ("a)", "unexpected `)`"),
1291            ("#1a", "identifier"),
1292            ("a.b*", "unexpected `*`"),
1293            ("[a='x\ny']", "unterminated string"),
1294        ] {
1295            let e = Selector::parse(s).expect_err(s);
1296            assert!(e.to_string().contains(msg), "{s:?}: {e}");
1297        }
1298        let deep = format!("{}a{}", ":not(".repeat(40), ")".repeat(40));
1299        assert!(
1300            Selector::parse(&deep)
1301                .unwrap_err()
1302                .to_string()
1303                .contains("nested deeper")
1304        );
1305        let long = vec!["a"; 300].join(" ");
1306        assert!(
1307            Selector::parse(&long)
1308                .unwrap_err()
1309                .to_string()
1310                .contains("too complex")
1311        );
1312        assert!(Selector::parse(&vec!["a"; 200].join(" ")).is_ok());
1313    }
1314
1315    #[test]
1316    fn descendant_backtracking_is_not_exponential() {
1317        let html = format!("{}{}", "<div>".repeat(400), "<span>x</span>");
1318        let doc = Html::parse(&html);
1319        let s: Selector = "div div div div div div div div div div p span"
1320            .parse()
1321            .unwrap();
1322        assert_eq!(doc.select(&s).count(), 0);
1323        let s: Selector = "div div div div div div div div div div span"
1324            .parse()
1325            .unwrap();
1326        assert_eq!(doc.select(&s).count(), 1);
1327    }
1328}