frame_sequence/
lib.rs

1//! A simple parser for frame sequences strings.
2//!
3//! This will parse a [`str`] describing a sequence of frames into
4//! a [`Vec`]`<`[`isize`]`>` containing individual frame numbers.
5//!
6//! Mainly intended/useful for rendering/animation applications.
7//!
8//! # Example Frame Sequence Strings
9//!
10//! Individual frames:
11//!
12//! `1,2,3,5,8,13` ⟶ `[1, 2, 3, 5, 8, 13]`
13//!
14//! A sequence:
15//!
16//! `10-15` ⟶ `[10, 11, 12, 13, 14, 15]`
17//!
18//! With step size:
19//!
20//! `10-20@2` ⟶ `[10, 12, 14, 16, 18, 20]`
21//!
22//! Step size must be always positive.
23//!
24//! To get a sequence backwards specify the range in reverse:
25//!
26//! `42-33@3` ⟶ `[42, 39, 36, 33]`
27//!
28//! With binary splitting:
29//!
30//! `10-20@b` ⟶ `[10, 20, 15, 12, 17, 11, 13, 16, 18, 14, 19]`
31//!
32//! The last frame of a sequence will be omitted if
33//! the specified step size does not touch it:
34//!
35//! `80-70@4` ⟶ `[80, 76, 72]`
36use itertools::Itertools;
37use pest::{
38    error::Error,
39    iterators::{Pair, Pairs},
40    Parser,
41};
42use pest_derive::Parser;
43use std::{cmp::Ordering, collections::HashSet};
44
45#[derive(Parser)]
46#[grammar = "frame_format_grammar.pest"]
47struct FrameSequenceParser;
48
49/// Parse a frame sequence string into a [`Vec`]`<`[`isize`]`>` of frames.
50///
51/// See the main page of the documentation for example `input` strings.
52pub fn parse_frame_sequence(input: &str) -> Result<Vec<isize>, Error<Rule>> {
53    FrameSequenceParser::parse(Rule::FrameSequenceString, input)
54        .map(|token_tree| remove_duplicates(frame_sequence_token_tree_to_frames(token_tree)))
55}
56
57fn chop(seq: &mut Vec<isize>, result: &mut Vec<isize>, elements: usize) {
58    if seq.len() < elements {
59        let mut new_seq = seq
60            .iter()
61            .tuple_windows()
62            .flat_map(|pair: (&isize, &isize)| {
63                let left = *pair.0;
64                let right = (*pair.0 + *pair.1) / 2;
65                if left < right {
66                    result.push(right);
67                    vec![left, right]
68                } else {
69                    vec![left]
70                }
71            })
72            .collect::<Vec<_>>();
73        new_seq.push(*seq.last().unwrap());
74        if new_seq.len() < elements {
75            chop(&mut new_seq, result, elements);
76        }
77        *seq = new_seq;
78    }
79}
80
81pub(crate) fn binary_sequence(range: (isize, isize)) -> Vec<isize> {
82    match range.0.cmp(&range.1) {
83        Ordering::Less => {
84            let mut seq = vec![range.0, range.1];
85            let mut result = seq.clone();
86            chop(&mut seq, &mut result, (range.1 - range.0) as _);
87            result
88        }
89        Ordering::Greater => {
90            let mut seq = vec![range.1, range.0];
91            let mut result = seq.clone();
92            chop(&mut seq, &mut result, (range.0 - range.1) as _);
93            result.reverse();
94            result
95        }
96        Ordering::Equal => vec![range.0],
97    }
98}
99
100fn frame_to_number(frame: Pair<Rule>) -> isize {
101    frame.as_str().parse::<isize>().unwrap()
102}
103
104fn frame_sequence_token_tree_to_frames(pairs: Pairs<Rule>) -> Vec<isize> {
105    pairs
106        .into_iter()
107        .flat_map(|pair| {
108            match pair.as_rule() {
109                Rule::FrameSequenceString | Rule::FrameSequence | Rule::FrameSequencePart => {
110                    frame_sequence_token_tree_to_frames(pair.into_inner())
111                }
112                Rule::FrameRange => {
113                    let mut pairs = pair.into_inner();
114                    let left = frame_to_number(pairs.next().unwrap());
115                    let right = frame_to_number(pairs.next().unwrap());
116
117                    // Do we have an `@`?
118                    if pairs.next().is_some() {
119                        let pair = pairs.next().unwrap();
120                        match pair.as_rule() {
121                            Rule::PositiveNumber => {
122                                let step = frame_to_number(pair);
123
124                                match left.cmp(&right) {
125                                    Ordering::Less => {
126                                        (left..right + 1).step_by(step as _).collect::<Vec<_>>()
127                                    }
128                                    Ordering::Greater => (right..left + 1)
129                                        .rev()
130                                        .step_by(step as _)
131                                        .collect::<Vec<_>>(),
132                                    Ordering::Equal => vec![left],
133                                }
134                            }
135                            Rule::BinarySequenceSymbol => binary_sequence((left, right)),
136                            _ => unreachable!(),
137                        }
138                    } else if left < right {
139                        (left..right + 1).collect::<Vec<_>>()
140                    } else if right < left {
141                        (right..left + 1).rev().collect::<Vec<_>>()
142                    }
143                    // left == right
144                    else {
145                        vec![left]
146                    }
147                }
148                Rule::Frame => vec![frame_to_number(pair)],
149                _ => vec![],
150            }
151        })
152        .collect::<Vec<_>>()
153}
154
155fn remove_duplicates(elements: Vec<isize>) -> Vec<isize> {
156    let mut set = HashSet::<isize>::new();
157    elements
158        .iter()
159        .filter_map(|e| {
160            if set.contains(e) {
161                None
162            } else {
163                set.insert(*e);
164                Some(*e)
165            }
166        })
167        .collect()
168}
169
170#[cfg(test)]
171mod tests {
172    #[test]
173    fn test_individual_frames() {
174        use crate::parse_frame_sequence;
175        let frames = parse_frame_sequence("1,2,3,5,8,13").unwrap();
176        assert_eq!([1, 2, 3, 5, 8, 13], frames.as_slice());
177    }
178
179    #[test]
180    fn test_frame_sequence() {
181        use crate::parse_frame_sequence;
182        let frames = parse_frame_sequence("10-15").unwrap();
183        assert_eq!([10, 11, 12, 13, 14, 15], frames.as_slice());
184    }
185
186    #[test]
187    fn test_fram_sequence_with_step() {
188        use crate::parse_frame_sequence;
189        let frames = parse_frame_sequence("10-20@2").unwrap();
190        assert_eq!([10, 12, 14, 16, 18, 20], frames.as_slice());
191    }
192
193    #[test]
194    fn test_frame_sequence_with_step_reversed() {
195        use crate::parse_frame_sequence;
196        let frames = parse_frame_sequence("42-33@3").unwrap();
197        assert_eq!([42, 39, 36, 33], frames.as_slice());
198    }
199
200    #[test]
201    fn test_binary_frame_sequence() {
202        use crate::parse_frame_sequence;
203        let frames = parse_frame_sequence("10-20@b").unwrap();
204        assert_eq!(
205            [10, 20, 15, 12, 17, 11, 13, 16, 18, 14, 19],
206            frames.as_slice()
207        );
208    }
209    
210    #[test]
211    fn test_multi_sequence() {
212        use crate::parse_frame_sequence;
213        let frames = parse_frame_sequence("10-20@2,42-33@3").unwrap();
214        assert_eq!([10, 12, 14, 16, 18, 20, 42, 39, 36, 33], frames.as_slice());
215    }
216    
217    #[test]
218    fn test_multi_sequence_x() {
219        use crate::parse_frame_sequence;
220        let frames = parse_frame_sequence("10-20x2,42-33x3").unwrap();
221        assert_eq!([10, 12, 14, 16, 18, 20, 42, 39, 36, 33], frames.as_slice());
222    }
223}