anathema-value-resolver 0.2.11

Anathema value resolver
Documentation
use crate::ValueKind;

pub(super) fn to_int<'bp>(args: &[ValueKind<'bp>]) -> ValueKind<'bp> {
    if args.len() != 1 {
        return ValueKind::Null;
    }

    match &args[0] {
        ValueKind::Int(i) => ValueKind::Int(*i),
        ValueKind::Float(f) => ValueKind::Int(*f as i64),
        ValueKind::Bool(b) if !b => ValueKind::Int(0),
        ValueKind::Bool(_) => ValueKind::Int(1),
        ValueKind::Char(c) => match c.to_digit(10) {
            Some(i) => ValueKind::Int(i as i64),
            None => ValueKind::Null,
        },
        ValueKind::Hex(hex) => ValueKind::Int(hex.as_u32() as i64),
        ValueKind::Str(s) => match s.parse() {
            Ok(i) => ValueKind::Int(i),
            Err(_) => ValueKind::Null,
        },
        ValueKind::Color(_)
        | ValueKind::Null
        | ValueKind::Map
        | ValueKind::Attributes
        | ValueKind::List(_)
        | ValueKind::DynList(_)
        | ValueKind::DynMap(_)
        | ValueKind::Range(..)
        | ValueKind::Composite(_) => ValueKind::Null,
    }
}

pub(super) fn to_float<'bp>(args: &[ValueKind<'bp>]) -> ValueKind<'bp> {
    if args.len() != 1 {
        return ValueKind::Null;
    }

    match &args[0] {
        ValueKind::Int(i) => ValueKind::Float(*i as f64),
        ValueKind::Float(f) => ValueKind::Float(*f),
        ValueKind::Bool(b) if !b => ValueKind::Float(0.0),
        ValueKind::Bool(_) => ValueKind::Float(1.0),
        ValueKind::Char(c) => match c.to_digit(10) {
            Some(i) => ValueKind::Float(i as f64),
            None => ValueKind::Null,
        },
        ValueKind::Hex(hex) => ValueKind::Float(hex.as_u32() as f64),
        ValueKind::Str(s) => match s.parse() {
            Ok(i) => ValueKind::Float(i),
            Err(_) => ValueKind::Null,
        },
        _ => ValueKind::Null,
    }
}

pub(super) fn round<'bp>(args: &[ValueKind<'bp>]) -> ValueKind<'bp> {
    if args.is_empty() || args.len() > 2 {
        return ValueKind::Null;
    }

    let precision = match to_int(&args[1..]) {
        ValueKind::Int(i) => i as usize,
        _ => 0,
    };

    match &args[0] {
        ValueKind::Float(f) => ValueKind::Str(format!("{f:.precision$}").into()),
        _ => ValueKind::Null,
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::functions::test::value;

    #[test]
    fn str_to_int() {
        let arg = value("123");
        let output = to_int(&[arg]);
        assert_eq!(ValueKind::Int(123), output);

        let arg = value("not a number");
        let output = to_int(&[arg]);
        assert_eq!(ValueKind::Null, output);
    }

    #[test]
    fn char_to_int() {
        let arg = value('4');
        let output = to_int(&[arg]);
        assert_eq!(ValueKind::Int(4), output);

        let arg = value('G');
        let output = to_int(&[arg]);
        assert_eq!(ValueKind::Null, output);
    }

    #[test]
    fn float_to_int() {
        let arg = value(1.1234);
        let output = to_int(&[arg]);
        assert_eq!(ValueKind::Int(1), output);
    }

    #[test]
    fn rounding_default_zero() {
        let args = [value(1.1234)];
        let output = round(&args);
        assert_eq!(value("1"), output);
    }

    #[test]
    fn rounding_with_arg() {
        let args = [value(1.1234), value(2)];
        let output = round(&args);
        assert_eq!(value("1.12"), output);
    }

    #[test]
    fn invalid_rounding() {
        let args = [value("1.1234"), value(2)];
        let output = round(&args);
        assert_eq!(ValueKind::Null, output);
    }
}