use crate::error::ValidationError;
use crate::number;
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
pub type Digit = u16;
#[derive(PartialEq, Clone, Debug)]
pub struct Number(number::Number);
impl Number {
pub fn new(digits: [Digit; 9]) -> Result<Self, ValidationError> {
Ok(Number(number::Number::new(digits)?))
}
pub fn checkdigit(&self) -> &Digit {
self.0.checkdigit()
}
pub fn digits(&self) -> &[Digit; 9] {
self.0.digits()
}
}
impl fmt::Display for Number {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let digits = self.0.digits();
write!(formatter, "{}", &digits[0])?;
write!(formatter, "{}", &digits[1])?;
write!(formatter, "{}", &digits[2])?;
if formatter.alternate() {
write!(formatter, " ")?;
}
write!(formatter, "{}", &digits[3])?;
write!(formatter, "{}", &digits[4])?;
write!(formatter, "{}", &digits[5])?;
if formatter.alternate() {
write!(formatter, " ")?;
}
write!(formatter, "{}", &digits[6])?;
write!(formatter, "{}", &digits[7])?;
write!(formatter, "{}", &digits[8])?;
write!(formatter, "{}", self.0.checkdigit())?;
Ok(())
}
}
impl TryFrom<&[Digit; 10]> for Number {
type Error = ValidationError;
fn try_from(value: &[Digit; 10]) -> Result<Self, Self::Error> {
let number = number::Number::try_from(value)?;
Ok(Number(number))
}
}
impl TryFrom<String> for Number {
type Error = ValidationError;
fn try_from(value: String) -> Result<Self, Self::Error> {
Number::from_str(&value)
}
}
impl TryFrom<usize> for Number {
type Error = ValidationError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
let number = number::Number::try_from(value)?;
Ok(Number(number))
}
}
impl FromStr for Number {
type Err = ValidationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let number = number::Number::from_str(s)?;
Ok(Number(number))
}
}
pub fn lottery() -> Result<Number, ValidationError> {
use rand::prelude::*;
let mut rng = rand::thread_rng();
let mut digits = [0u16; 9];
let distr = rand::distributions::Uniform::new_inclusive(0, 9);
for x in &mut digits {
*x = rng.sample(distr);
}
match Number::new(digits) {
Err(_) => lottery(),
number => number,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_formatted_string() -> Result<(), ValidationError> {
let f = Number::from_str("893 177 4583")?;
let u = Number::from_str("8931774583")?;
assert_eq!(f.checkdigit(), u.checkdigit());
assert_eq!(f.digits(), u.digits());
Ok(())
}
#[test]
fn display_compact() -> Result<(), ValidationError> {
let n = "893 177 4583";
let number = Number::from_str(n)?;
let expected = "8931774583";
assert_eq!(format!("{}", number), expected.to_string());
Ok(())
}
#[test]
fn display_alternate() -> Result<(), ValidationError> {
let n = String::from("893 177 4583");
let number = Number::from_str(&n)?;
assert_eq!(format!("{:#}", number), n);
Ok(())
}
}