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        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(pair::new(
351                Rc::clone(&self.queue),
352                self.input,
353                Rc::clone(&self.line_index),
354                self.start,
355            ))
356        } else {
357            None
358        }
359    }
360
361    /// Generates a string that stores the lexical information of `self` in
362    /// a pretty-printed JSON format.
363    #[cfg(feature = "pretty-print")]
364    pub fn to_json(&self) -> String {
365        ::serde_json::to_string_pretty(self).expect("Failed to pretty-print Pairs to json.")
366    }
367
368    fn pair(&self) -> usize {
369        match self.queue[self.start] {
370            QueueableToken::Start {
371                end_token_index, ..
372            } => end_token_index,
373            _ => unreachable!(),
374        }
375    }
376
377    fn pair_from_end(&self) -> usize {
378        match self.queue[self.end - 1] {
379            QueueableToken::End {
380                start_token_index, ..
381            } => start_token_index,
382            _ => unreachable!(),
383        }
384    }
385
386    fn pos(&self, index: usize) -> usize {
387        match self.queue[index] {
388            QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } => {
389                input_pos
390            }
391        }
392    }
393}
394
395impl<'i, R: RuleType> ExactSizeIterator for Pairs<'i, R> {
396    #[inline]
397    fn len(&self) -> usize {
398        self.pairs_count
399    }
400}
401
402impl<'i, R: RuleType> Iterator for Pairs<'i, R> {
403    type Item = Pair<'i, R>;
404
405    fn next(&mut self) -> Option<Self::Item> {
406        let pair = self.peek()?;
407
408        self.start = self.pair() + 1;
409        self.pairs_count -= 1;
410        Some(pair)
411    }
412
413    fn size_hint(&self) -> (usize, Option<usize>) {
414        let len = <Self as ExactSizeIterator>::len(self);
415        (len, Some(len))
416    }
417}
418
419impl<'i, R: RuleType> DoubleEndedIterator for Pairs<'i, R> {
420    fn next_back(&mut self) -> Option<Self::Item> {
421        if self.end <= self.start {
422            return None;
423        }
424
425        self.end = self.pair_from_end();
426        self.pairs_count -= 1;
427
428        let pair = pair::new(
429            Rc::clone(&self.queue),
430            self.input,
431            Rc::clone(&self.line_index),
432            self.end,
433        );
434
435        Some(pair)
436    }
437}
438
439impl<'i, R: RuleType> fmt::Debug for Pairs<'i, R> {
440    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441        f.debug_list().entries(self.clone()).finish()
442    }
443}
444
445impl<'i, R: RuleType> fmt::Display for Pairs<'i, R> {
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        write!(
448            f,
449            "[{}]",
450            self.clone()
451                .map(|pair| format!("{}", pair))
452                .collect::<Vec<_>>()
453                .join(", ")
454        )
455    }
456}
457
458impl<'i, R: PartialEq> PartialEq for Pairs<'i, R> {
459    fn eq(&self, other: &Pairs<'i, R>) -> bool {
460        Rc::ptr_eq(&self.queue, &other.queue)
461            && ptr::eq(self.input, other.input)
462            && self.start == other.start
463            && self.end == other.end
464    }
465}
466
467impl<'i, R: Eq> Eq for Pairs<'i, R> {}
468
469impl<'i, R: Hash> Hash for Pairs<'i, R> {
470    fn hash<H: Hasher>(&self, state: &mut H) {
471        (&*self.queue as *const Vec<QueueableToken<'i, R>>).hash(state);
472        (self.input as *const str).hash(state);
473        self.start.hash(state);
474        self.end.hash(state);
475    }
476}
477
478#[cfg(feature = "pretty-print")]
479impl<'i, R: RuleType> ::serde::Serialize for Pairs<'i, R> {
480    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
481    where
482        S: ::serde::Serializer,
483    {
484        let start = self.pos(self.start);
485        let end = self.pos(self.end - 1);
486        let pairs = self.clone().collect::<Vec<_>>();
487
488        let mut ser = serializer.serialize_struct("Pairs", 2)?;
489        ser.serialize_field("pos", &(start, end))?;
490        ser.serialize_field("pairs", &pairs)?;
491        ser.end()
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::super::super::macros::tests::*;
498    use super::super::super::Parser;
499    use alloc::borrow::ToOwned;
500    use alloc::boxed::Box;
501    use alloc::format;
502    use alloc::vec;
503    use alloc::vec::Vec;
504
505    #[test]
506    #[cfg(feature = "pretty-print")]
507    fn test_pretty_print() {
508        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
509
510        let expected = r#"{
511  "pos": [
512    0,
513    5
514  ],
515  "pairs": [
516    {
517      "pos": [
518        0,
519        3
520      ],
521      "rule": "a",
522      "inner": {
523        "pos": [
524          1,
525          2
526        ],
527        "pairs": [
528          {
529            "pos": [
530              1,
531              2
532            ],
533            "rule": "b",
534            "inner": "b"
535          }
536        ]
537      }
538    },
539    {
540      "pos": [
541        4,
542        5
543      ],
544      "rule": "c",
545      "inner": "e"
546    }
547  ]
548}"#;
549
550        assert_eq!(expected, pairs.to_json());
551    }
552
553    #[test]
554    fn as_str() {
555        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
556
557        assert_eq!(pairs.as_str(), "abcde");
558    }
559
560    #[test]
561    fn get_input_of_pairs() {
562        let input = "abcde";
563        let pairs = AbcParser::parse(Rule::a, input).unwrap();
564
565        assert_eq!(pairs.get_input(), input);
566    }
567
568    #[test]
569    fn as_str_empty() {
570        let mut pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
571
572        assert_eq!(pairs.nth(1).unwrap().into_inner().as_str(), "");
573    }
574
575    #[test]
576    fn concat() {
577        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
578
579        assert_eq!(pairs.concat(), "abce");
580    }
581
582    #[test]
583    fn pairs_debug() {
584        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
585
586        #[rustfmt::skip]
587        assert_eq!(
588            format!("{:?}", pairs),
589            "[\
590                Pair { rule: a, span: Span { str: \"abc\", start: 0, end: 3 }, inner: [\
591                    Pair { rule: b, span: Span { str: \"b\", start: 1, end: 2 }, inner: [] }\
592                ] }, \
593                Pair { rule: c, span: Span { str: \"e\", start: 4, end: 5 }, inner: [] }\
594            ]"
595            .to_owned()
596        );
597    }
598
599    #[test]
600    fn pairs_display() {
601        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
602
603        assert_eq!(
604            format!("{}", pairs),
605            "[a(0, 3, [b(1, 2)]), c(4, 5)]".to_owned()
606        );
607    }
608
609    #[test]
610    fn iter_for_pairs() {
611        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
612        assert_eq!(
613            pairs.map(|p| p.as_rule()).collect::<Vec<Rule>>(),
614            vec![Rule::a, Rule::c]
615        );
616    }
617
618    #[test]
619    fn double_ended_iter_for_pairs() {
620        let pairs = AbcParser::parse(Rule::a, "abcde").unwrap();
621        assert_eq!(
622            pairs.rev().map(|p| p.as_rule()).collect::<Vec<Rule>>(),
623            vec![Rule::c, Rule::a]
624        );
625    }
626
627    #[test]
628    fn test_line_col() {
629        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
630        let pair = pairs.next().unwrap();
631        assert_eq!(pair.as_str(), "abc");
632        assert_eq!(pair.line_col(), (1, 1));
633
634        let pair = pairs.next().unwrap();
635        assert_eq!(pair.as_str(), "e");
636        assert_eq!(pair.line_col(), (2, 1));
637
638        let pair = pairs.next().unwrap();
639        assert_eq!(pair.as_str(), "fgh");
640        assert_eq!(pair.line_col(), (2, 2));
641    }
642
643    #[test]
644    fn test_rev_iter_line_col() {
645        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap().rev();
646        let pair = pairs.next().unwrap();
647        assert_eq!(pair.as_str(), "fgh");
648        assert_eq!(pair.line_col(), (2, 2));
649
650        let pair = pairs.next().unwrap();
651        assert_eq!(pair.as_str(), "e");
652        assert_eq!(pair.line_col(), (2, 1));
653
654        let pair = pairs.next().unwrap();
655        assert_eq!(pair.as_str(), "abc");
656        assert_eq!(pair.line_col(), (1, 1));
657    }
658
659    #[test]
660    // false positive: pest uses `..` as a complete range (historically)
661    #[allow(clippy::almost_complete_range)]
662    fn test_tag_node_branch() {
663        use crate::{state, ParseResult, ParserState};
664        #[allow(non_camel_case_types)]
665        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
666        enum Rule {
667            number, // 0..9
668            add,    // num + num
669            mul,    // num * num
670        }
671        fn mark_branch(
672            state: Box<ParserState<'_, Rule>>,
673        ) -> ParseResult<Box<ParserState<'_, Rule>>> {
674            expr(state, Rule::mul, "*")
675                .and_then(|state| state.tag_node("mul"))
676                .or_else(|state| expr(state, Rule::add, "+"))
677                .and_then(|state| state.tag_node("add"))
678        }
679        fn expr<'a>(
680            state: Box<ParserState<'a, Rule>>,
681            r: Rule,
682            o: &'static str,
683        ) -> ParseResult<Box<ParserState<'a, Rule>>> {
684            state.rule(r, |state| {
685                state.sequence(|state| {
686                    number(state)
687                        .and_then(|state| state.tag_node("lhs"))
688                        .and_then(|state| state.match_string(o))
689                        .and_then(number)
690                        .and_then(|state| state.tag_node("rhs"))
691                })
692            })
693        }
694        fn number(state: Box<ParserState<'_, Rule>>) -> ParseResult<Box<ParserState<'_, Rule>>> {
695            state.rule(Rule::number, |state| state.match_range('0'..'9'))
696        }
697        let input = "1+2";
698        let pairs = state(input, mark_branch).unwrap();
699        assert_eq!(pairs.find_first_tagged("add").unwrap().as_rule(), Rule::add);
700        assert_eq!(pairs.find_first_tagged("mul"), None);
701
702        let mut left_numbers = pairs.clone().find_tagged("lhs");
703
704        assert_eq!(left_numbers.next().unwrap().as_str(), "1");
705        assert_eq!(left_numbers.next(), None);
706        let mut right_numbers = pairs.find_tagged("rhs");
707
708        assert_eq!(right_numbers.next().unwrap().as_str(), "2");
709        assert_eq!(right_numbers.next(), None);
710    }
711
712    #[test]
713    fn exact_size_iter_for_pairs() {
714        let pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
715        assert_eq!(pairs.len(), pairs.count());
716
717        let pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap().rev();
718        assert_eq!(pairs.len(), pairs.count());
719
720        let mut pairs = AbcParser::parse(Rule::a, "abc\nefgh").unwrap();
721        let pairs_len = pairs.len();
722        let _ = pairs.next().unwrap();
723        assert_eq!(pairs.count() + 1, pairs_len);
724    }
725}