letter_sequence/sequence/
combined.rs

1//! This file models a combined sequences.
2
3use super::*;
4use crate::sequence::renderer::RenderDisplay;
5use std::convert::From;
6use std::convert::TryFrom;
7use std::fmt::Display;
8use std::iter::Iterator;
9
10/// A `CombinedSequence` models any sequence which is not entirely either
11/// numbers, uppercase, or lowercase. It does this by combining sequences which
12/// are. Each internal sequence it contains has its own state. A
13/// `CombinedSequence` gives you a method to increment and display a set of
14/// sequences.
15/// 
16/// ```rust
17/// use letter_sequence::{ CombinedSequence, TryFrom };
18/// 
19/// // When created the default is for the last element in the sequence to
20/// // grow, here the first "Z" from the left turns into "AA"
21/// let mut seq = CombinedSequence::try_from("Z9Z").unwrap();
22/// let elem = seq.next().unwrap();
23/// assert_eq!( elem.to_string(), "Z9Z" );
24/// let elem = seq.next().unwrap();
25/// assert_eq!( elem.to_string(), "AA0A" );
26///
27/// // here we the right has two characters "ZZ", the number will only be
28/// // incremeneted after this point.
29/// let mut seq = CombinedSequence::try_from("9ZZ").unwrap();
30/// let elem = seq.next().unwrap();
31/// assert_eq!( elem.to_string(), "9ZZ" );
32/// let elem = seq.next().unwrap();
33/// assert_eq!( elem.to_string(), "10A" );
34///
35/// // let elem = seq.nth( 26usize.pow(2) - 1 + 25 ).unwrap(); // 0 based- 26 letters in the alphabet
36/// // assert_eq!( elem.to_string(), "10ZZ" );
37/// ```
38#[derive(Default, Debug, Clone)]
39pub struct CombinedSequence {
40	inner: Vec<Sequence>,
41	done: bool,
42	initialized: bool,
43}
44
45
46impl CombinedSequence {
47	fn new() -> Self {
48		Self::default()
49	}
50	pub fn push(&mut self, seq: Sequence) {
51		self.inner.push(seq)
52	}
53	
54	/// Returns an iterator over the internal sequences
55	pub fn sequences(&self) -> std::slice::Iter<Sequence> {
56		self.inner.iter()
57	}
58	/// Returns a mutable iterator over the internal seqeunces
59	pub fn sequences_mut(&mut self) -> std::slice::IterMut<Sequence> {
60		self.inner.iter_mut()
61	}
62
63	/// Reset a CombinedSequence to the initial state.
64	/// ```
65	/// use letter_sequence::{ CombinedSequence, TryFrom };
66	///
67	/// let mut seq = CombinedSequence::try_from("9ZZ").unwrap();
68	/// let elem = seq.next().unwrap();
69	/// assert_eq!( elem.to_string(), "9ZZ" );
70	/// let elem = seq.next().unwrap();
71	/// assert_eq!( elem.to_string(), "10A" );
72	/// seq.reset();
73	/// let elem = seq.next().unwrap();
74	/// assert_eq!( elem.to_string(), "9ZZ" );
75	/// ```
76	pub fn reset(&mut self) {
77		self.initialized = false;
78		for seq in &mut self.sequences_mut() {
79			seq.reset()
80		}
81	}
82	
83	/// Zero out a CombinedSequence. This ignores the initial state.
84	/// ```
85	/// use letter_sequence::{ CombinedSequence, TryFrom };
86	///
87	/// let mut seq = CombinedSequence::try_from("9ZZ").unwrap();
88	/// seq.zero();
89	/// let elem = seq.next().unwrap();
90	/// assert_eq!( elem.to_string(), "0A" );
91	/// ```
92	pub fn zero(&mut self) {
93		self.initialized = false;
94		for seq in &mut self.sequences_mut() {
95			seq.zero()
96		}
97	}
98	
99	fn generate_element(&self) -> CombinedElement {
100		let mut ce = CombinedElement::new();
101		for s in self.sequences() {
102			ce.push(s.generate_element())
103		}
104		ce
105	}
106
107}
108
109impl Iterator for CombinedSequence {
110	type Item = CombinedElement;
111
112	/// Combined sequences will roll over when calling `.next()`.
113	/// ```
114	/// use letter_sequence::{ CombinedSequence, TryFrom };
115	/// 
116	/// let mut seq = CombinedSequence::try_from("A9Z").unwrap();
117	/// let elem = seq.next().unwrap();
118	/// assert_eq!( elem.to_string(), "A9Z" );
119	/// let elem = seq.next().unwrap();
120	/// assert_eq!( elem.to_string(), "B0A" );
121	/// ```
122	fn next(&mut self) -> Option<Self::Item> {
123		match (self.done, self.initialized) {
124			(_, false) => {
125				for s in &mut self.sequences_mut() {
126					s.next();
127				}
128				self.initialized = true;
129				Some(self.generate_element())
130			}
131			(true, _) => { dbg!("done = true"); None },
132			(false, true) => {
133				for s in &mut self.sequences_mut() {
134					match s.next() {
135						None => s.rollover(),
136						Some(_) => break
137					};
138				}
139				Some(self.generate_element())
140			},
141		}
142	}
143}
144
145impl From<Vec<Sequence>> for CombinedSequence {
146	fn from(vec: Vec<Sequence>) -> Self {
147		Self { inner: vec, .. Default::default() }
148	}
149}
150
151impl TryFrom<&str> for CombinedSequence {
152	type Error = SequenceError;
153
154	fn try_from(input: &str) -> Result<CombinedSequence, Self::Error> {
155		let mut input : &mut str = &mut input.to_owned();
156		if ! input.is_ascii() {
157			return Err(SequenceError::InvalidString);
158		}
159
160		let mut splits: Vec<(usize, RenderDisplay)> = Vec::new();
161		let mut iter = input.char_indices();
162		while let Some((i, val)) = iter.next() {
163			let val = RenderDisplay::try_from(val)?;
164			match splits.last() {
165				// current character is rendered differently from last character
166				Some((_, lastval)) if *lastval != val => splits.push( (i,val) ),
167				// first character seen
168				None => splits.push( (i,val) ),
169				_ => ()
170			}
171		}
172
173		let mut cs = CombinedSequence::new();
174		while let Some((idx, _)) = splits.pop() {
175			let (init, last) = input.split_at_mut(idx);
176			input = &mut(*init);
177			let mut seq = SequenceBuilder::try_from(&*last).unwrap();
178			if ! splits.is_empty() {
179				let length = last.len();
180				seq = seq.max_render_capacity(length as u8);
181			}
182			cs.push( seq.build()? );
183		}
184		Ok(cs)
185	}
186}
187
188#[derive(Debug, Default, Clone)]
189pub struct CombinedElement {
190	inner: Vec<Element>
191}
192
193impl CombinedElement {
194	fn new() -> Self {
195		Self::default()
196	}
197	pub fn push(&mut self, elem: Element) {
198		self.inner.push(elem)
199	}
200	/// Returns an iterator over the internal sequences
201	pub fn elements(&self) -> std::slice::Iter<Element> {
202		self.inner.iter()
203	}
204}
205
206impl Display for CombinedElement {
207	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208		for i in self.elements().rev() {
209			write!(f, "{}", i )?
210		}
211		Ok(())
212	}
213}
214
215
216#[cfg(test)]
217mod test {
218	use super::*;
219
220
221	/// Ensure parity between a CombinedSequence and a Sequence
222	#[test]
223	fn one_char() {
224		let mut seq = SequenceBuilder::try_from("A").unwrap().build().unwrap();
225		let mut cs = CombinedSequence::try_from("A").unwrap();
226		let elem = seq.next().unwrap();
227		let celem = cs.next().unwrap();
228		assert_eq!( elem.to_string(), "A" );
229		assert_eq!( celem.to_string(), "A" );
230
231		let elem = seq.next().unwrap();
232		let celem = cs.next().unwrap();
233		assert_eq!( elem.to_string(), "B" );
234		assert_eq!( celem.to_string(), "B" );
235	}
236	
237	#[test]
238	fn two_char() {
239		let mut seq = CombinedSequence::try_from("A0").unwrap();
240		let elem = seq.next().unwrap();
241		assert_eq!( elem.to_string(), "A0" );
242		let elem = seq.next().unwrap();
243		assert_eq!( elem.to_string(), "A1" );
244	}
245
246	#[test]
247	fn segment_access() {
248		let mut seq = CombinedSequence::try_from("A9").unwrap();
249		let segment1 = seq.inner[0].next().unwrap();
250		assert_eq!( segment1.to_string(), "9" );
251		let segment1 = seq.inner[0].next();
252		assert_eq!( segment1, None );
253	}
254
255
256	#[test]
257	fn one_char_with_rollover() {
258		let mut seq = CombinedSequence::try_from("9").unwrap();
259		let elem = seq.next().unwrap();
260		assert_eq!( elem.to_string(), "9" );
261		let elem = seq.next().unwrap();
262		assert_eq!( elem.to_string(), "10" );
263		
264		let mut seq = CombinedSequence::try_from("Z").unwrap();
265		let elem = seq.next().unwrap();
266		assert_eq!( elem.to_string(), "Z" );
267		let elem = seq.next().unwrap();
268		assert_eq!( elem.to_string(), "AA" );
269	}
270	
271	#[test]
272	fn two_char_with_rollover1() {
273		let mut seq = CombinedSequence::try_from("A9").unwrap();
274		let elem = seq.next().unwrap();
275		assert_eq!( elem.to_string(), "A9" );
276		let elem = seq.next().unwrap();
277		assert_eq!( elem.to_string(), "B0" );
278		let elem = seq.next().unwrap();
279		assert_eq!( elem.to_string(), "B1" );
280		
281		let mut seq = CombinedSequence::try_from("9A").unwrap();
282		let elem = seq.next().unwrap();
283		assert_eq!( elem.to_string(), "9A" );
284		let elem = seq.next().unwrap();
285		assert_eq!( elem.to_string(), "9B" );
286		let elem = seq.next().unwrap();
287		assert_eq!( elem.to_string(), "9C" );
288		
289		let mut seq = CombinedSequence::try_from("Z9ZZ").unwrap();
290		let elem = seq.next().unwrap();
291		assert_eq!( elem.to_string(), "Z9ZZ" );
292		let elem = seq.next().unwrap();
293		assert_eq!( elem.to_string(), "AA0A" );
294	}
295}