letter-sequence 2.1.0

A method to create sequence displayed as uppercase or lower letters, or digits
Documentation
pub mod builder;
pub mod combined;
pub mod helper;
pub mod renderer;

pub use element::Element;

use std::convert::Into;

use super::*;
use crate::sequence::renderer::*;
use std::fmt;

use std::ops::{AddAssign, SubAssign};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Sequence {
	// start is needed a sequence of 9 will rollover to 0, but the starting
	// state will be 9.
	start: u64,
	end: Option<u64>,
	inner: Option<u64>,
	step: u32,
	// If you hard .set() something, next call to .next() returns that element
	initialized: bool,
	// Forces .next() to return None
	done: bool,
	renderer: SeqRenderer,
}

impl Default for Sequence {
	fn default() -> Self {
		Self {
			initialized: false,
			start: 0,
			end: None,
			inner: None,
			done: false,
			renderer: Default::default(),
			step: 1,
		}
	}
}

impl Iterator for Sequence {
	type Item = Element;

	fn next(&mut self) -> Option<Self::Item> {
		match (self.done, self.initialized, self.inner) {
			(true, _, _) => None,
			(false, false, Some(_)) => {
				self.initialized = true;
				Some(self.generate_element())
			}
			(false, _, None) => {
				self.initialized = true;
				match self.inner(self.start) {
					Ok(_) => Some(self.generate_element()),
					_ => None
				}
			},
			(false, true, Some(_)) => {
				match self.add(self.step as u64) {
					Ok(_) => Some(self.generate_element()),
					Err(_) => { self.done = true; None }
				}
			}
		}
	}
}

impl fmt::Display for Sequence {
	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
		write!(f, "{}", 42 ) // self.renderer.render(self.inner).unwrap())
	}
}

/// ```rust
/// ```
impl Sequence {
	/// Create a new instance, as above, but do not check for violations of
	/// display
	fn new_unchecked(inner: u64) -> Self {
		Self {
			start: inner,
			..Self::default()
		}
	}

	/// Reset the sequence to 0
	pub fn reset(&mut self) {
		self.inner = None;
		self.done = false;
	}
	
	pub fn zero(&mut self) {
		self.initialized = false;
		self.inner = Some(0);
		self.done = false;
	}
	
	pub fn rollover(&mut self) {
		self.initialized = true;
		self.inner = Some(0);
		self.done = false;
	}

	/// Add rhs to sequence
	pub fn add(&mut self, rhs: u64) -> Result<(), SequenceError> {
		match (self.done, self.inner) {
			(true, _) => Err(SequenceError::Done),
			(false, None) => Err(SequenceError::Uninitialized),
			(false, Some(inner)) => match inner.checked_add(rhs) {
				None => return Err(SequenceError::OutOfRange),
				Some(new_inner) => self.inner(new_inner)
			}
		}
	}

	/// This should probably check that the result is > 0
	pub fn sub(&mut self, rhs: u64) -> Result<(), SequenceError> {
		match (self.done, self.inner) {
			(true, _) => Err(SequenceError::Done),
			(false, None) => Err(SequenceError::Uninitialized),
			(false, Some(inner)) => match inner.checked_sub(rhs) {
				None => return Err(SequenceError::OutOfRange),
				Some(new_inner) => self.inner(new_inner)
			}
		}
	}

	pub fn set(&mut self, inner: u64) -> Option<Element> {
		self.initialized = false;
		self.inner(inner).map( |_| self.generate_element()).ok()
	}

	fn inner(&mut self, new_inner: u64) -> Result<(), SequenceError> {
		// Calculated max render exhausted
		if let Some(renderlimit) = self.renderer.max() {
			if renderlimit < new_inner {
				return Err(SequenceError::OutOfRangeRender);
			}
		}
		// User imposed end of sequence
		if let Some(end) = self.end {
			if end <= new_inner {
				return Err(SequenceError::OutOfRange);
			}
		}
		self.inner = Some(new_inner);
		Ok(())
	}

	fn generate_element(&self) -> Element {
		element::ElementBuilder::default()
			.inner(self.inner.unwrap())
			.renderer(self.renderer.clone())
			.build()
			.unwrap()
	}
}

impl<T: Into<u64>> AddAssign<T> for Sequence {
	fn add_assign(&mut self, rhs: T) {
		self.initialized = false;
		self.add(rhs.into()).expect("Failed to call add_assisgn on sequence");
	}
}

impl<T: Into<u64>> SubAssign<T> for Sequence {
	fn sub_assign(&mut self, rhs: T) {
		self.initialized = false;
		self.sub(rhs.into()).expect("Failed to call sub_assign on sequence");
	}
}

#[cfg(test)]
mod test {
	use super::*;

	#[test]
	fn default() {
		let mut seq = Sequence::default();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "0");
	}

	#[test]
	fn new_unchecked() {
		let mut seq = Sequence::new_unchecked(u64::MAX);
		let elem = seq.next().unwrap();
		assert_eq!(
			elem.to_string(),
			u64::MAX.to_string()
		);
	}

	#[test]
	fn num() {
		for i in 0..123_456 {
			let mut seq = SequenceBuilder::new().start(i).build().unwrap();
			let elem = seq.next().unwrap();
			assert_eq!(elem.to_string(), i.to_string());
		}
	}

	#[test]
	fn lower() {
		for i in 0..100 {
			let mut seq = SequenceBuilder::new().start(i).lower().build().unwrap();
			let elem = seq.next().unwrap();
			assert_eq!(
				elem.to_string().matches(|c: char| !c.is_lowercase()).count(),
				0
			);
			assert_eq!(seq.to_string(), seq.to_string().to_ascii_lowercase(),)
		}
	}

	#[test]
	fn upper() {
		for i in 0..100 {
			let mut seq = SequenceBuilder::new().start(i).upper().build().unwrap();
			let elem = seq.next().unwrap();
			assert_eq!(
				elem.to_string().matches(|c: char| !c.is_uppercase()).count(),
				0
			);
			assert_eq!(seq.to_string(), seq.to_string().to_ascii_uppercase(),)
		}
	}

	//inner: 1 * 26u64.pow(2) + 1 * 26u64.pow(1)  + 0 * 26u64.pow(0),
	#[test]
	fn rollover() {
		use std::convert::TryFrom;
		let mut seq = SequenceBuilder::try_from("Z").unwrap().build().unwrap();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "Z");
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "AA");
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "AB");

		let mut seq = SequenceBuilder::try_from("z").unwrap().build().unwrap();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "z");
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "aa");
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "ab");

		let mut seq = SequenceBuilder::try_from("9").unwrap().build().unwrap();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), (9).to_string());
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), (10).to_string());
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), (11).to_string());
	}

	#[test]
	fn zero() {
		use std::convert::TryFrom;
		let mut seq = SequenceBuilder::try_from(0).unwrap().build().unwrap();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "0");
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "1");
		seq.zero();
		let elem = seq.next().unwrap();
		assert_eq!(elem.to_string(), "0");
	}

	#[test]
	fn assignmod() {
		let mut seq = SequenceBuilder::new().start(42).build().unwrap();

		let elem = seq.next().unwrap();
		assert_eq!( elem.to_string(), "42" );

		// += and -= for unsigned int
		seq += 0u32;
		let elem = seq.next().unwrap();
		assert_eq!( elem.to_string(), "42" );

		seq += 5u32;
		let elem = seq.next().unwrap();
		assert_eq!( elem.to_string(), "47" );

		seq -= 5u32;
		let elem = seq.next().unwrap();
		assert_eq!( elem.to_string(), "42" );
	}
}