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> {
validate(&digits)?;
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 {
self.0.fmt(formatter)
}
}
impl TryFrom<&[Digit; 10]> for Number {
type Error = ValidationError;
fn try_from(value: &[Digit; 10]) -> Result<Self, Self::Error> {
Ok(Number(number::Number::try_from(value)?))
}
}
impl TryFrom<String> for Number {
type Error = ValidationError;
fn try_from(value: String) -> Result<Self, Self::Error> {
let number = number::Number::try_from(value)?;
validate(number.digits())?;
Ok(Number(number))
}
}
impl TryFrom<usize> for Number {
type Error = ValidationError;
fn try_from(value: usize) -> Result<Self, Self::Error> {
let number = number::Number::try_from(value)?;
validate(number.digits())?;
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)?;
validate(number.digits())?;
Ok(Number(number))
}
}
fn validate(digits: &[u16; 9]) -> Result<(), ValidationError> {
let day = digits[0] * 10 + digits[1];
let month = digits[2] * 10 + digits[3];
if day == 0 || day > 31 || month == 0 || month > 12 {
return Err(ValidationError::new("Invalid CHI number"));
}
Ok(())
}
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,
}
}