skillet 0.6.2

Skillet: micro expression language (arithmetic, logical, functions, arrays, conditionals, excel formulas) made in Rust bin cli and server
Documentation
use crate::ast::TypeName;
use crate::error::Error;
use crate::types::Value;

pub fn cast_value(v: Value, ty: &TypeName) -> Result<Value, Error> {
    Ok(match ty {
        TypeName::Float => match v {
            Value::Number(n) => Value::Number(n),
            Value::Currency(n) => Value::Number(n),
            Value::String(s) => Value::Number(
                s.parse::<f64>()
                    .map_err(|_| Error::new("Cannot cast String to Float", None))?,
            ),
            Value::Boolean(b) => Value::Number(if b { 1.0 } else { 0.0 }),
            Value::Null => Value::Number(0.0),
            _ => return Err(Error::new("Cannot cast to Float", None)),
        },
        TypeName::Integer => match v {
            Value::Number(n) => Value::Number((n as i64) as f64),
            Value::Currency(n) => Value::Number((n as i64) as f64),
            Value::String(s) => {
                let mut clean_s = String::new();
                let mut has_dot = false;
                for (i, c) in s.chars().enumerate() {
                    if i == 0 && (c == '-' || c == '+') {
                        clean_s.push(c);
                    } else if c.is_ascii_digit() {
                        clean_s.push(c);
                    } else if c == '.' && !has_dot {
                        clean_s.push(c);
                        has_dot = true;
                    } else {
                        break;
                    }
                }
                Value::Number(
                    clean_s.parse::<f64>()
                        .unwrap_or(0.0)
                        .trunc(),
                )
            },
            Value::Boolean(b) => Value::Number(if b { 1.0 } else { 0.0 }),
            Value::Null => Value::Number(0.0),
            _ => return Err(Error::new("Cannot cast to Integer", None)),
        },
        TypeName::String => match v {
            Value::String(s) => Value::String(s),
            Value::Number(n) => Value::String(n.to_string()),
            Value::Boolean(b) => Value::String(if b { "TRUE".into() } else { "FALSE".into() }),
            Value::Null => Value::String(String::new()),
            Value::Array(items) => Value::String(format!("{:?}", items)),
            Value::Currency(n) => Value::String(format!("{:.4}", n)),
            Value::DateTime(ts) => Value::String(ts.to_string()),
            Value::Json(s) => Value::String(s),
        },
        TypeName::Boolean => match v {
            Value::Boolean(b) => Value::Boolean(b),
            Value::Number(n) => Value::Boolean(n != 0.0),
            Value::Currency(n) => Value::Boolean(n != 0.0),
            Value::String(s) => Value::Boolean(!s.trim().is_empty()),
            Value::Array(items) => Value::Boolean(!items.is_empty()),
            Value::Null => Value::Boolean(false),
            Value::DateTime(ts) => Value::Boolean(ts != 0),
            Value::Json(s) => Value::Boolean(!s.trim().is_empty()),
        },
        TypeName::Array => match v {
            Value::Array(items) => Value::Array(items),
            other => Value::Array(vec![other]),
        },
        TypeName::Currency => match v {
            Value::Currency(n) => Value::Currency(n),
            Value::Number(n) => Value::Currency(n),
            Value::String(s) => Value::Currency(
                s.parse::<f64>()
                    .map_err(|_| Error::new("Cannot cast String to Currency", None))?,
            ),
            Value::Boolean(b) => Value::Currency(if b { 1.0 } else { 0.0 }),
            Value::Null => Value::Currency(0.0),
            _ => return Err(Error::new("Cannot cast to Currency", None)),
        },
        TypeName::DateTime => match v {
            Value::DateTime(ts) => Value::DateTime(ts),
            Value::Number(n) => Value::DateTime(n as i64),
            Value::String(s) => Value::DateTime(
                s.parse::<i64>()
                    .map_err(|_| Error::new("Cannot cast String to DateTime", None))?,
            ),
            _ => return Err(Error::new("Cannot cast to DateTime", None)),
        },
        TypeName::Json => match v {
            Value::Json(s) => Value::Json(s),
            Value::String(s) => Value::Json(s),
            Value::Number(n) => Value::Json(n.to_string()),
            Value::Boolean(b) => Value::Json(if b {
                "true".to_string()
            } else {
                "false".to_string()
            }),
            Value::Null => Value::Json("null".to_string()),
            Value::Currency(n) => Value::Json(n.to_string()),
            Value::DateTime(ts) => Value::Json(ts.to_string()),
            Value::Array(items) => {
                let json_items: Result<Vec<String>, Error> = items
                    .iter()
                    .map(|item| cast_value(item.clone(), &TypeName::Json))
                    .map(|result| {
                        result.map(|v| match v {
                            Value::Json(s) => s,
                            _ => unreachable!(),
                        })
                    })
                    .collect();
                match json_items {
                    Ok(strings) => Value::Json(format!("[{}]", strings.join(","))),
                    Err(e) => return Err(e),
                }
            }
        },
    })
}