use std::fmt::Display;
use lyn::Scanner;
use super::Error;
#[derive(Debug, PartialEq, Clone)]
pub struct Decimal(String);
impl TryFrom<&str> for Decimal {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut scanner = Scanner::new(value);
match decimal(&mut scanner) {
Ok(entered) => {
if entered {
if scanner.is_done() {
Ok(Self(value.into()))
} else {
Err(Error::DecimalFormat)
}
} else {
Err(Error::DecimalFormat)
}
}
Err(err) => Err(err),
}
}
}
impl Default for Decimal {
fn default() -> Self {
Self("0".into())
}
}
impl From<&Decimal> for f32 {
fn from(value: &Decimal) -> Self {
value.0.parse::<f32>().expect("f32 string")
}
}
impl From<f32> for Decimal {
fn from(value: f32) -> Self {
Self(value.to_string())
}
}
impl From<&Decimal> for f64 {
fn from(value: &Decimal) -> Self {
value.0.parse::<f64>().expect("f64 string")
}
}
impl Display for Decimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
fn decimal(scanner: &mut Scanner) -> Result<bool, Error> {
sign(scanner)?;
nonnegative_decimal(scanner)
}
fn sign(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(match scanner.peek() {
Some('+' | '-') => {
scanner.pop();
true
}
_ => false,
})
}
fn nonnegative_decimal(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(if nonnegative_integer(scanner)? {
fractional_part(scanner)?;
true
} else {
false
})
}
fn nonnegative_integer(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(positive_integer(scanner)? || zero(scanner)?)
}
fn fractional_part(scanner: &mut Scanner) -> Result<bool, Error> {
if dot(scanner)? {
if digits(scanner)? {
Ok(true)
} else {
Err(Error::DecimalFormat)
}
} else {
Ok(false)
}
}
fn positive_integer(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(if nonzero_digit(scanner)? {
digits(scanner)?;
true
} else {
false
})
}
fn digits(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(if digit(scanner)? {
loop {
if !digit(scanner)? {
break true;
}
}
} else {
false
})
}
fn digit(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(nonzero_digit(scanner)? | zero(scanner)?)
}
fn nonzero_digit(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(match scanner.peek() {
Some('1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') => {
scanner.pop();
true
}
_ => false,
})
}
fn zero(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(scanner.take(&'0'))
}
fn dot(scanner: &mut Scanner) -> Result<bool, Error> {
Ok(scanner.take(&'.'))
}
#[cfg(test)]
pub mod decimal {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn alpha() {
let mut scanner = Scanner::new("abc");
assert_eq!(decimal(&mut scanner), Ok(false))
}
#[test]
fn zero() {
let mut scanner = Scanner::new("0");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn plus_zero() {
let mut scanner = Scanner::new("+0");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn minus_zero() {
let mut scanner = Scanner::new("-0");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn one() {
let mut scanner = Scanner::new("1");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn plus_one() {
let mut scanner = Scanner::new("+1");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn minus_one() {
let mut scanner = Scanner::new("-1");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn zero_one() {
let mut scanner = Scanner::new("01");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn point() {
let mut scanner = Scanner::new(".");
assert_eq!(decimal(&mut scanner), Ok(false))
}
#[test]
fn point_zero() {
let mut scanner = Scanner::new(".0");
assert_eq!(decimal(&mut scanner), Ok(false))
}
#[test]
fn zero_point() {
let mut scanner = Scanner::new("0.");
assert_eq!(decimal(&mut scanner), Err(Error::DecimalFormat))
}
#[test]
fn zero_point_zero() {
let mut scanner = Scanner::new("0.0");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn zero_point_one() {
let mut scanner = Scanner::new("0.1");
assert_eq!(decimal(&mut scanner), Ok(true))
}
#[test]
fn zero_point_zero_x() {
let mut scanner = Scanner::new("0.0x");
assert_eq!(decimal(&mut scanner), Ok(true))
}
}
#[cfg(test)]
pub mod float_from_str {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn alpha() {
assert_eq!(Decimal::try_from("abc"), Err(Error::DecimalFormat))
}
#[test]
fn zero_point_zero_x() {
assert_eq!(Decimal::try_from("0.0x"), Err(Error::DecimalFormat))
}
#[test]
fn valid() {
assert_eq!(
Decimal::try_from("+3.14159"),
Ok(Decimal("+3.14159".into()))
)
}
}
#[cfg(test)]
pub mod f32_from_float {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test() {
let float = Decimal::try_from("1.23").unwrap();
assert_eq!(f32::from(&float), 1.23f32)
}
}
#[cfg(test)]
pub mod float_from_f32 {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test() {
assert_eq!(Decimal::from(1.23f32), Decimal::try_from("1.23").unwrap())
}
}
#[cfg(test)]
pub mod f64_from_float {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test() {
let float = Decimal::try_from("1.23").unwrap();
assert_eq!(f64::from(&float), 1.23)
}
}