pub mod config;
pub mod ui;
use std::{
num::ParseIntError,
ops::{Add, AddAssign, Sub, SubAssign},
str::FromStr,
};
use num_bigint::{BigInt, BigUint};
use thiserror::Error;
use crate::{
aux::{Common, Owned},
runtime::model::{Dir, Pair},
};
#[derive(Owned!)]
pub struct Money(pub Natural);
#[derive(Owned!)]
pub struct Balance(pub Integer);
pub type Natural = BigUint;
pub type Integer = BigInt;
impl Balance {
pub fn flip(&mut self) {
self.0 *= -1;
}
pub fn abs(self) -> Money {
Money(self.0.into_parts().1)
}
pub fn take_order(&mut self, dir: Dir) -> Pair {
if !dir.would_reorder() {
self.flip()
}
dir.into()
}
}
impl From<Money> for Balance {
fn from(amount: Money) -> Self {
Self(amount.0.into())
}
}
macro_rules! calc {
(+ $lhs:ty, $rhs:ty $( => $inner:ty )? ) => {
impl Add<$rhs> for $lhs {
type Output = Self;
fn add(self, rhs: $rhs) -> Self {
Self(self.0 + $(<$inner>::from)?(rhs.0))
}
}
};
(- $lhs:ty, $rhs:ty $( => $inner:ty )? ) => {
impl Sub<$rhs> for $lhs {
type Output = Self;
fn sub(self, rhs: $rhs) -> Self {
Self(self.0 - $(<$inner>::from)?(rhs.0))
}
}
};
(+= $lhs:ty, $rhs:ty ) => {
impl AddAssign<$rhs> for $lhs {
fn add_assign(&mut self, rhs: $rhs) {
*self = self.clone() + rhs;
}
}
};
(-= $lhs:ty, $rhs:ty ) => {
impl SubAssign<$rhs> for $lhs {
fn sub_assign(&mut self, rhs: $rhs) {
*self = self.clone() - rhs;
}
}
};
}
calc!(+ Money, Money);
calc!(-Money, Money);
calc!(+= Money, Money);
calc!(-= Money, Money);
calc!(+ Balance, Balance);
calc!(-Balance, Balance);
calc!(+= Balance, Balance);
calc!(-= Balance, Balance);
calc!(+ Balance, Money => Integer);
calc!(- Balance, Money => Integer);
calc!(+= Balance, Money);
calc!(-= Balance, Money);
#[derive(Owned!)]
pub struct Debit {
pub between: Dir,
pub amount: Money,
}
#[derive(Common!)]
pub struct Gtin(u64);
impl Gtin {
pub const MAX_DIGITS: u8 = 14;
pub const MAX: Self = Self(10u64.pow(Self::MAX_DIGITS as u32) - 1);
pub fn new(source: u64) -> Result<Self, TooLongError> {
let gtin = Self(source);
if gtin.digits() > Self::MAX_DIGITS {
return Err(TooLongError {
orig: source,
n: gtin.digits(),
});
}
Ok(gtin)
}
pub fn get(&self) -> u64 {
self.0
}
pub fn digits(&self) -> u8 {
let n = self.0;
if n == 0 { 1 } else { n.ilog10() as u8 + 1 }
}
}
impl FromStr for Gtin {
type Err = GtinParseError;
fn from_str(source: &str) -> Result<Self, Self::Err> {
let source = source.parse()?;
Self::new(source).map_err(GtinParseError::TooLong)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum GtinParseError {
#[error("couldn't parse as an integer: {0}")]
ExpectedInteger(#[from] ParseIntError),
#[error("valid int, but too long: {0}")]
TooLong(TooLongError),
}
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error(
"`{orig}` contains {n} digits while it can contain {} digits at most",
Gtin::MAX_DIGITS
)]
pub struct TooLongError {
pub orig: u64,
pub n: u8,
}