use super::*;
use crate::sequence::helper::*;
use std::convert::TryFrom;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RenderDisplay {
Upper,
Lower,
Numeric,
}
impl Default for RenderDisplay {
fn default() -> Self { RenderDisplay::Numeric }
}
impl TryFrom<char> for RenderDisplay {
type Error = SequenceError;
fn try_from(c: char) -> Result<Self, Self::Error> {
if c.is_ascii_digit() {
Ok(RenderDisplay::Numeric)
}
else if c.is_ascii_lowercase() {
Ok(RenderDisplay::Lower)
}
else if c.is_ascii_uppercase() {
Ok(RenderDisplay::Upper)
}
else {
Err(SequenceError::InvalidCharacter(c))
}
}
}
impl TryFrom<&str> for RenderDisplay {
type Error = SequenceError;
fn try_from(str: &str) -> Result<Self, Self::Error> {
let char = str.chars().next();
match char {
Some(char) => Ok(RenderDisplay::try_from(char)?),
None => Err(SequenceError::EmptyString)
}
}
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub struct SeqRenderer {
display: RenderDisplay,
capacity: Option<u8>,
length: Option<u8>,
}
impl SeqRenderer {
pub fn display(self) -> RenderDisplay { self.display }
pub fn render(&self, x: u64) -> Result<String, SequenceError> {
let result = match self.display {
RenderDisplay::Upper => int_to_string(x)?,
RenderDisplay::Lower => {
let mut display = int_to_string(x)?;
display.make_ascii_lowercase();
display.to_string()
}
RenderDisplay::Numeric => x.to_string()
};
if let Some(capacity) = self.capacity {
if result.len() > capacity as usize {
return Err(SequenceError::OutOfRange)
}
}
match (self.length, self.display) {
(None, _) => Ok(result),
(Some(length), RenderDisplay::Numeric) => Ok(format!("{:0>len$}", result, len=length as usize)),
(_, RenderDisplay::Upper | RenderDisplay::Lower) => Err(SequenceError::PaddingAlpha),
}
}
pub fn max(&self) -> Option<u64> {
if let Some(char_limit) = self.capacity {
match self.display {
RenderDisplay::Upper | RenderDisplay::Lower => Some(max_alpha(char_limit)),
RenderDisplay::Numeric => Some(max_numeric(char_limit)),
}
}
else {
None
}
}
}
#[derive(Default, Debug)]
pub struct SeqRendererBuilder {
display: RenderDisplay,
capacity: Option<u8>,
length: Option<u8>,
}
impl SeqRendererBuilder {
pub fn upper() -> Self { Self::new_with_display( RenderDisplay::Upper) }
pub fn lower() -> Self { Self::new_with_display( RenderDisplay::Lower) }
pub fn numeric() -> Self { Self::new_with_display( RenderDisplay::Numeric) }
pub fn set_upper(self) -> Self { self.set_display(RenderDisplay::Upper) }
pub fn set_lower(self) -> Self { self.set_display(RenderDisplay::Lower) }
pub fn set_numeric(self) -> Self { self.set_display(RenderDisplay::Numeric) }
pub fn set_display(mut self, display: RenderDisplay) -> Self {
self.display = display;
self
}
fn new_with_display(display: RenderDisplay) -> Self { Self { display, ..Default::default() } }
pub fn capacity(mut self, val: u8) -> Self { self.capacity = Some(val); self }
pub fn length(mut self, val: u8) -> Self { self.length = Some(val); self }
pub fn build(self) -> Result<SeqRenderer,SequenceError> {
let sr = SeqRenderer {
capacity: self.capacity,
display: self.display,
length: self.length,
.. Default::default()
};
Ok(sr)
}
}
pub fn max_alpha(chars: u8) -> u64 {
const BASE : u64 = 26;
const Z: u32 = 25;
let mut max: u64 = 0;
for x in 0..chars {
max += (1 + Z) as u64 * BASE.pow(x as u32)
}
max -= 1;
max
}
pub fn max_numeric(chars: u8) -> u64 {
10u64.pow(chars as u32) - 1
}