pest/iterators/
pairs.rs

1// pest. The Elegant Parser
2// Copyright (c) 2018 DragoÈ™ Tiselice
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10use alloc::format;
11use alloc::rc::Rc;
12use alloc::string::String;
13use alloc::vec::Vec;
14use core::fmt;
15use core::hash::{Hash, Hasher};
16use core::iter::Filter;
17use core::ptr;
18use core::str;
19
20#[cfg(feature = "pretty-print")]
21use serde::ser::SerializeStruct;
22
23use super::flat_pairs::{self, FlatPairs};
24use super::line_index::LineIndex;
25use super::pair::{self, Pair};
26use super::queueable_token::QueueableToken;
27use super::tokens::{self, Tokens};
28use crate::RuleType;
29
30/// An iterator over [`Pair`]s. It is created by [`pest::state`] and [`Pair::into_inner`].
31///
32/// [`Pair`]: struct.Pair.html
33/// [`pest::state`]: ../fn.state.html
34/// [`Pair::into_inner`]: struct.Pair.html#method.into_inner
35#[derive(Clone)]
36pub struct Pairs<'i, R> {
37    queue: Rc<Vec<QueueableToken<'i, R>>>,
38    input: &'i str,
39    start: usize,
40    end: usize,
41    pairs_count: usize,
42    line_index: Rc<LineIndex>,
43}
44
45pub fn new<'i, R: RuleType>(
46    queue: Rc<Vec<QueueableToken<'i, R>>>,
47    input: &'i str,
48    line_index: Option<Rc<LineIndex>>,
49    start: usize,
50    end: usize,
51) -> Pairs<'i, R> {
52    let line_index = match line_index {
53        Some(line_index) => line_index,
54        None => Rc::new(LineIndex::new(input)),
55    };
56
57    let mut pairs_count = 0;
58    let mut cursor = start;
59    while cursor < end {
60        cursor = match queue[cursor] {
61            QueueableToken::Start {
62                end_token_index, ..
63            } => end_token_index,
64            _ => unreachable!(),
65        } + 1;
66        pairs_count += 1;
67    }
68
69    Pairs {
70        queue,
71        input,
72        start,
73        end,
74        pairs_count,
75        line_index,
76    }
77}
78
79impl<'i, R: RuleType> Pairs<'i, R> {
80    /// Captures a slice from the `&str` defined by the starting position of the first token `Pair`
81    /// and the ending position of the last token `Pair` of the `Pairs`. This also captures
82    /// the input between those two token `Pair`s.
83    ///
84    /// # Examples
85    ///
86    /// ```
87    /// # use std::rc::Rc;
88    /// # use pest;
89    /// # #[allow(non_camel_case_types)]
90    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
91    /// enum Rule {
92    ///     a,
93    ///     b
94    /// }
95    ///
96    /// let input = "a b";
97    /// let pairs = pest::state(input, |state| {
98    ///     // generating Token pairs with Rule::a and Rule::b ...
99    /// #     state.rule(Rule::a, |s| s.match_string("a")).and_then(|s| s.skip(1))
100    /// #         .and_then(|s| s.rule(Rule::b, |s| s.match_string("b")))
101    /// }).unwrap();
102    ///
103    /// assert_eq!(pairs.as_str(), "a b");
104    /// ```
105    #[inline]
106    pub fn as_str(&self) -> &'i str {
107        if self.start < self.end {
108            let start = self.pos(self.start);
109            let end = self.pos(self.end - 1);
110            // Generated positions always come from Positions and are UTF-8 borders.
111            &self.input[start..end]
112        } else {
113            ""
114        }
115    }
116
117    /// Returns the input string of `Pairs`.
118    ///
119    /// This function returns the input string of `Pairs` as a `&str`. This is the source string
120    /// from which `Pairs` was created. The returned `&str` can be used to examine the contents of
121    /// `Pairs` or to perform further processing on the string.
122    ///
123    /// # Examples
124    ///
125    /// ```
126    /// # use std::rc::Rc;
127    /// # use pest;
128    /// # #[allow(non_camel_case_types)]
129    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
130    /// enum Rule {
131    ///     a,
132    ///     b
133    /// }
134    ///
135    /// // Example: Get input string from Pairs
136    ///
137    /// let input = "a b";
138    /// let pairs = pest::state(input, |state| {
139    ///     // generating Token pairs with Rule::a and Rule::b ...
140    /// #     state.rule(Rule::a, |s| s.match_string("a")).and_then(|s| s.skip(1))
141    /// #         .and_then(|s| s.rule(Rule::b, |s| s.match_string("b")))
142    /// }).unwrap();
143    ///
144    /// assert_eq!(pairs.as_str(), "a b");
145    /// assert_eq!(input, pairs.get_input());
146    /// ```
147    pub fn get_input(&self) -> &'i str {
148        self.input
149    }
150
151    /// Captures inner token `Pair`s and concatenates resulting `&str`s. This does not capture
152    /// the input between token `Pair`s.
153    ///
154    /// # Examples
155    ///
156    /// ```
157    /// # use std::rc::Rc;
158    /// # use pest;
159    /// # #[allow(non_camel_case_types)]
160    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
161    /// enum Rule {
162    ///     a,
163    ///     b
164    /// }
165    ///
166    /// let input = "a b";
167    /// let pairs = pest::state(input, |state| {
168    ///     // generating Token pairs with Rule::a and Rule::b ...
169    /// #     state.rule(Rule::a, |s| s.match_string("a")).and_then(|s| s.skip(1))
170    /// #         .and_then(|s| s.rule(Rule::b, |s| s.match_string("b")))
171    /// }).unwrap();
172    ///
173    /// assert_eq!(pairs.concat(), "ab");
174    /// ```
175    #[inline]
176    pub fn concat(&self) -> String {
177        self.clone()
178            .fold(String::new(), |string, pair| string + pair.as_str())
179    }
180
181    /// Flattens the `Pairs`.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// # use std::rc::Rc;
187    /// # use pest;
188    /// # #[allow(non_camel_case_types)]
189    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
190    /// enum Rule {
191    ///     a,
192    ///     b
193    /// }
194    ///
195    /// let input = "";
196    /// let pairs = pest::state(input, |state| {
197    ///     // generating nested Token pair with Rule::b inside Rule::a
198    /// #     state.rule(Rule::a, |state| {
199    /// #         state.rule(Rule::b, |s| Ok(s))
200    /// #     })
201    /// }).unwrap();
202    /// let tokens: Vec<_> = pairs.flatten().tokens().collect();
203    ///
204    /// assert_eq!(tokens.len(), 4);
205    /// ```
206    #[inline]
207    pub fn flatten(self) -> FlatPairs<'i, R> {
208        unsafe { flat_pairs::new(self.queue, self.input, self.start, self.end) }
209    }
210
211    /// Finds the first pair that has its node or branch tagged with the provided
212    /// label. Searches in the flattened [`Pairs`] iterator.
213    ///
214    /// # Examples
215    ///
216    /// Try to recognize the branch between add and mul
217    /// ```
218    /// use pest::{state, ParseResult, ParserState};
219    /// #[allow(non_camel_case_types)]
220    /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
221    /// enum Rule {
222    ///     number, // 0..9
223    ///     add,    // num + num
224    ///     mul,    // num * num
225    /// }
226    /// fn mark_branch(
227    ///     state: Box<ParserState<'_, Rule>>,
228    /// ) -> ParseResult<Box<ParserState<'_, Rule>>> {
229    ///     expr(state, Rule::mul, "*")
230    ///         .and_then(|state| state.tag_node("mul"))
231    ///         .or_else(|state| expr(state, Rule::add, "+"))
232    ///         .and_then(|state| state.tag_node("add"))
233    /// }
234    /// fn expr<'a>(
235    ///     state: Box<ParserState<'a, Rule>>,
236    ///     r: Rule,
237    ///     o: &'static str,
238    /// ) -> ParseResult<Box<ParserState<'a, Rule>>> {
239    ///     state.rule(r, |state| {
240    ///         state.sequence(|state| {
241    ///             number(state)
242    ///                 .and_then(|state| state.tag_node("lhs"))
243    ///                 .and_then(|state| state.match_string(o))
244    ///                 .and_then(number)
245    ///                 .and_then(|state| state.tag_node("rhs"))
246    ///         })
247    ///     })
248    /// }
249    /// fn number(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
250    ///     state.rule(Rule::number, |state| state.match_range('0'..'9'))
251    /// }
252    /// let input = "1+2";
253    /// let pairs = state(input, mark_branch).unwrap();
254    /// assert_eq!(pairs.find_first_tagged("add").unwrap().as_rule(), Rule::add);
255    /// assert_eq!(pairs.find_first_tagged("mul"), None);
256    /// ```
257    #[inline]
258    pub fn find_first_tagged(&self, tag: &'i str) -> Option<Pair<'i, R>> {
259        self.clone().find_tagged(tag).next()
260    }
261
262    /// Returns the iterator over pairs that have their node or branch tagged
263    /// with the provided label. The iterator is built from a flattened [`Pairs`] iterator.
264    ///
265    /// # Examples
266    ///
267    /// Try to recognize the node between left and right hand side
268    /// ```
269    /// use pest::{state, ParseResult, ParserState};
270    /// #[allow(non_camel_case_types)]
271    /// #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
272    /// enum Rule {
273    ///     number, // 0..9
274    ///     add,    // num + num
275    ///     mul,    // num * num
276    /// }
277    /// fn mark_branch(
278    ///     state: Box<ParserState<'_, Rule>>,
279    /// ) -> ParseResult<Box<ParserState<'_, Rule>>> {
280    ///     expr(state, Rule::mul, "*")
281    ///         .and_then(|state| state.tag_node("mul"))
282    ///         .or_else(|state| expr(state, Rule::add, "+"))
283    ///         .and_then(|state| state.tag_node("add"))
284    /// }
285    /// fn expr<'a>(
286    ///     state: Box<ParserState<'a, Rule>>,
287    ///     r: Rule,
288    ///     o: &'static str,
289    /// ) -> ParseResult<Box<ParserState<'a, Rule>>> {
290    ///     state.rule(r, |state| {
291    ///         state.sequence(|state| {
292    ///             number(state)
293    ///                 .and_then(|state| state.tag_node("lhs"))
294    ///                 .and_then(|state| state.match_string(o))
295    ///                 .and_then(number)
296    ///                 .and_then(|state| state.tag_node("rhs"))
297    ///         })
298    ///     })
299    /// }
300    /// fn number(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
301    ///     state.rule(Rule::number, |state| state.match_range('0'..'9'))
302    /// }
303    ///
304    /// let input = "1+2";
305    /// let pairs = state(input, mark_branch).unwrap();
306    /// let mut left_numbers = pairs.find_tagged("lhs");
307    /// assert_eq!(left_numbers.next().unwrap().as_str(), "1");
308    /// assert_eq!(left_numbers.next(), None);
309    /// ```
310    #[inline]
311    pub fn find_tagged(
312        self,
313        tag: &'i str,
314    ) -> Filter<FlatPairs<'i, R>, impl FnMut(&Pair<'i, R>) -> bool + '_> {
315        self.flatten()
316            .filter(move |pair: &Pair<'i, R>| matches!(pair.as_node_tag(), Some(nt) if nt == tag))
317    }
318
319    /// Returns the `Tokens` for the `Pairs`.
320    ///
321    /// # Examples
322    ///
323    /// ```
324    /// # use std::rc::Rc;
325    /// # use pest;
326    /// # #[allow(non_camel_case_types)]
327    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
328    /// enum Rule {
329    ///     a
330    /// }
331    ///
332    /// let input = "";
333    /// let pairs = pest::state(input, |state| {
334    ///     // generating Token pair with Rule::a ...
335    /// #     state.rule(Rule::a, |s| Ok(s))
336    /// }).unwrap();
337    /// let tokens: Vec<_> = pairs.tokens().collect();
338    ///
339    /// assert_eq!(tokens.len(), 2);
340    /// ```
341    #[inline]
342    pub fn tokens(self) -> Tokens<'i, R> {
343        tokens::new(self.queue, self.input, self.start, self.end)
344    }
345
346    /// Peek at the first inner `Pair` without changing the position of this iterator.
347    #[inline]
348    pub fn peek(&self) -> Option<Pair<'i, R>> {
349        if self.start < self.end {
350            Some(unsafe {
351                pair::new(
352                    Rc::clone(&self.queue),
353                    self.input,
354                    Rc::clone(&self.line_index),
355                    self.start,
356                )
357            })
358        } else {
359            None
360        }
361    }
362
363    /// Generates a string that stores the lexical information of `self` in
364    /// a pretty-printed JSON format.
365    #[cfg(feature = "pretty-print")]
366    pub fn to_json(&self) -> String {
367        ::serde_json::to_string_pretty(self).expect("Failed to pretty-print Pairs to json.")
368    }
369
370    fn pair(&self) -> usize {
371        match self.queue[self.start] {
372            QueueableToken::Start {
373                end_token_index, ..
374            } => end_token_index,
375            _ => unreachable!(),
376        }
377    }
378
379    fn pair_from_end(&self) -> usize {
380        match self.queue[self.end - 1] {
381            QueueableToken::End {
382                start_token_index, ..
383            } => start_token_index,
384            _ => unreachable!(),
385        }
386    }
387
388    fn pos(&self, index: usize) -> usize {
389        match self.queue[index] {
390            QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } => {
391                input_pos
392            }
393        }
394    }
395}
396
397impl<'i, R: RuleType> ExactSizeIterator for Pairs<'i, R> {
398    #[inline]
399    fn len(&self) -> usize {
400        self.pairs_count
401    }
402}
403
404impl<'i, R: RuleType> Iterator for Pairs<'i, R> {
405    type Item = Pair<'i, R>;
406
407    fn next(&mut self) -> Option<Self::Item> {
408        let pair = self.peek()?;
409
410        self.start = self.pair() + 1;
411        self.pairs_count -= 1;
412        Some(pair)
413    }
414
415    fn size_hint(&self) -> (usize, Option<usize>) {
416        let len = <Self as ExactSizeIterator>::len(self);
417        (len, Some(len))
418    }
419}
420
421impl<'i, R: RuleType> DoubleEndedIterator for Pairs<'i, R> {
422    fn next_back(&mut self) -> Option<Self::Item> {
423        if self.end <= self.start {
424            return None;
425        }
426
427        self.end = self.pair_from_end();
428        self.pairs_count -= 1;
429
430        let pair = unsafe {
431            pair::new(
432                Rc::clone(&self.queue),
433                self.input,
434                Rc::clone(&self.line_index),
435                self.end,
436            )
437        };
438
439        Some(pair)
440    }
441}
442
443impl<'i, R: RuleType> fmt::Debug for Pairs<'i, R> {
444    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445        f.debug_list().entries(self.clone()).finish()
446    }
447}
448
449impl<'i, R: RuleType> fmt::Display for Pairs<'i, R> {
450    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451        write!(
452            f,
453            "[{}]",
454            self.clone()
455                .map(|pair| format!("{}", pair))
456                .collect::<Vec<_>>()
457                .join(", ")
458        )
459    }
460}
461
462impl<'i, R: PartialEq> PartialEq for Pairs<'i, R> {
463    fn eq(&self, other: &Pairs<'i, R>) -> bool {
464        Rc::ptr_eq(&self.queue, &other.queue)
465            && ptr::eq(self.input, other.input)
466            && self.start == other.start
467            && self.end == other.end
468    }
469}
470
471impl<'i, R: Eq> Eq for Pairs<'i, R> {}
472
473impl<'i, R: Hash> Hash for Pairs<'i, R> {
474    fn hash<H: Hasher>(&self, state: &mut H) {
475        (&*self.queue as *const Vec<QueueableToken<'i, R>>).hash(state);
476        (self.input as *const str).hash(state);
477        self.start.hash(state);
478        self.end.hash(state);
479    }
480}
481
482#[cfg(feature = "pretty-print")]
483impl<'i, R: RuleType> ::serde::Serialize for Pairs<'i, R> {
484    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
485    where
486        S: ::serde::Serializer,
487    {
488        let start = self.pos(self.start);
489        let end = self.pos(self.end - 1);
490        let pairs = self.clone().collect::<Vec<_>>();
491
492        let mut ser = serializer.serialize_struct("Pairs", 2)?;
493        ser.serialize_field("pos", &(start, end))?;
494        ser.serialize_field("pairs", &pairs)?;
495        ser.end()
496    }
497}
498
499#[cfg(test)]
500mod tests {
501    use super::super::super::macros::tests::*;
502    use super::super::super::Parser;
503    use alloc::borrow::ToOwned;
504    use alloc::boxed::Box;
505    use alloc::format;
506    use alloc::vec;
507    use alloc::vec::Vec;
508
509    #[test]
510    #[cfg(feature = "pretty-print")]
511    fn test_pretty_print() {
512        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
513
514        let expected = r#"{
515  "pos": [
516    0,
517    5
518  ],
519  "pairs": [
520    {
521      "pos": [
522        0,
523        3
524      ],
525      "rule": "a",
526      "inner": {
527        "pos": [
528          1,
529          2
530        ],
531        "pairs": [
532          {
533            "pos": [
534              1,
535              2
536            ],
537            "rule": "b",
538            "inner": "b"
539          }
540        ]
541      }
542    },
543    {
544      "pos": [
545        4,
546        5
547      ],
548      "rule": "c",
549      "inner": "e"
550    }
551  ]
552}"#;
553
554        assert_eq!(expected, pairs.to_json());
555    }
556
557    #[test]
558    fn as_str() {
559        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
560
561        assert_eq!(pairs.as_str(), "abcde");
562    }
563
564    #[test]
565    fn get_input_of_pairs() {
566        let input = "abcde";
567        let pairs = AbcParser::parse(Rule::a, input).unwrap();
568
569        assert_eq!(pairs.get_input(), input);
570    }
571
572    #[test]
573    fn as_str_empty() {
574        let mut pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
575
576        assert_eq!(pairs.nth(1).unwrap().into_inner().as_str(), "");
577    }
578
579    #[test]
580    fn concat() {
581        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
582
583        assert_eq!(pairs.concat(), "abce");
584    }
585
586    #[test]
587    fn pairs_debug() {
588        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
589
590        #[rustfmt::skip]
591        assert_eq!(
592            format!("{:?}", pairs),
593            "[\
594                Pair { rule: a, span: Span { str: \"abc\", start: 0, end: 3 }, inner: [\
595                    Pair { rule: b, span: Span { str: \"b\", start: 1, end: 2 }, inner: [] }\
596                ] }, \
597                Pair { rule: c, span: Span { str: \"e\", start: 4, end: 5 }, inner: [] }\
598            ]"
599            .to_owned()
600        );
601    }
602
603    #[test]
604    fn pairs_display() {
605        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
606
607        assert_eq!(
608            format!("{}", pairs),
609            "[a(0, 3, [b(1, 2)]), c(4, 5)]".to_owned()
610        );
611    }
612
613    #[test]
614    fn iter_for_pairs() {
615        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
616        assert_eq!(
617            pairs.map(|p| p.as_rule()).collect::<Vec<Rule>>(),
618            vec![Rule::a, Rule::c]
619        );
620    }
621
622    #[test]
623    fn double_ended_iter_for_pairs() {
624        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
625        assert_eq!(
626            pairs.rev().map(|p| p.as_rule()).collect::<Vec<Rule>>(),
627            vec![Rule::c, Rule::a]
628        );
629    }
630
631    #[test]
632    fn test_line_col() {
633        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
634        let pair = pairs.next().unwrap();
635        assert_eq!(pair.as_str(), "abc");
636        assert_eq!(pair.line_col(), (1, 1));
637
638        let pair = pairs.next().unwrap();
639        assert_eq!(pair.as_str(), "e");
640        assert_eq!(pair.line_col(), (2, 1));
641
642        let pair = pairs.next().unwrap();
643        assert_eq!(pair.as_str(), "fgh");
644        assert_eq!(pair.line_col(), (2, 2));
645    }
646
647    #[test]
648    fn test_rev_iter_line_col() {
649        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap().rev();
650        let pair = pairs.next().unwrap();
651        assert_eq!(pair.as_str(), "fgh");
652        assert_eq!(pair.line_col(), (2, 2));
653
654        let pair = pairs.next().unwrap();
655        assert_eq!(pair.as_str(), "e");
656        assert_eq!(pair.line_col(), (2, 1));
657
658        let pair = pairs.next().unwrap();
659        assert_eq!(pair.as_str(), "abc");
660        assert_eq!(pair.line_col(), (1, 1));
661    }
662
663    #[test]
664    // false positive: pest uses `..` as a complete range (historically)
665    #[allow(clippy::almost_complete_range)]
666    fn test_tag_node_branch() {
667        use crate::{state, ParseResult, ParserState};
668        #[allow(non_camel_case_types)]
669        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
670        enum Rule {
671            number, // 0..9
672            add,    // num + num
673            mul,    // num * num
674        }
675        fn mark_branch(
676            state: Box<ParserState<'_, Rule>>,
677        ) -> ParseResult<Box<ParserState<'_, Rule>>> {
678            expr(state, Rule::mul, "*")
679                .and_then(|state| state.tag_node("mul"))
680                .or_else(|state| expr(state, Rule::add, "+"))
681                .and_then(|state| state.tag_node("add"))
682        }
683        fn expr<'a>(
684            state: Box<ParserState<'a, Rule>>,
685            r: Rule,
686            o: &'static str,
687        ) -> ParseResult<Box<ParserState<'a, Rule>>> {
688            state.rule(r, |state| {
689                state.sequence(|state| {
690                    number(state)
691                        .and_then(|state| state.tag_node("lhs"))
692                        .and_then(|state| state.match_string(o))
693                        .and_then(number)
694                        .and_then(|state| state.tag_node("rhs"))
695                })
696            })
697        }
698        fn number(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
699            state.rule(Rule::number, |state| state.match_range('0'..'9'))
700        }
701        let input = "1+2";
702        let pairs = state(input, mark_branch).unwrap();
703        assert_eq!(pairs.find_first_tagged("add").unwrap().as_rule(), Rule::add);
704        assert_eq!(pairs.find_first_tagged("mul"), None);
705
706        let mut left_numbers = pairs.clone().find_tagged("lhs");
707
708        assert_eq!(left_numbers.next().unwrap().as_str(), "1");
709        assert_eq!(left_numbers.next(), None);
710        let mut right_numbers = pairs.find_tagged("rhs");
711
712        assert_eq!(right_numbers.next().unwrap().as_str(), "2");
713        assert_eq!(right_numbers.next(), None);
714    }
715
716    #[test]
717    fn exact_size_iter_for_pairs() {
718        let pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
719        assert_eq!(pairs.len(), pairs.count());
720
721        let pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap().rev();
722        assert_eq!(pairs.len(), pairs.count());
723
724        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
725        let pairs_len = pairs.len();
726        let _ = pairs.next().unwrap();
727        assert_eq!(pairs.count() + 1, pairs_len);
728    }
729}