expr-lang 1.1.1

Implementation of expr language in Rust
Documentation
use crate::{bail, Environment, Value};

pub fn add_string_functions(env: &mut Environment) {
    env.add_function("trim", |c| {
        if c.args.len() != 1 && c.args.len() != 2 {
            bail!("trim() takes one or two arguments");
        }
        if let (Value::String(s), None) = (&c.args[0], c.args.get(1)) {
            Ok(s.trim().into())
        } else if let (Value::String(s), Some(Value::String(chars))) = (&c.args[0], c.args.get(1)) {
            Ok(s.trim_matches(|c| chars.contains(c)).into())
        } else {
            bail!("trim() takes a string as the first argument and an optional string of characters to trim");
        }
    });

    env.add_function("trimPrefix", |c| {
        if let (Value::String(s), Value::String(prefix)) = (&c.args[0], &c.args[1]) {
            Ok(s.strip_prefix(prefix).unwrap_or(s).into())
        } else {
            bail!("trimPrefix() takes a string as the first argument and a string to trim as the second argument");
        }
    });

    env.add_function("trimSuffix", |c| {
        if let (Value::String(s), Value::String(suffix)) = (&c.args[0], &c.args[1]) {
            Ok(s.strip_suffix(suffix).unwrap_or(s).into())
        } else {
            bail!("trimSuffix() takes a string as the first argument and a string to trim as the second argument");
        }
    });

    env.add_function("upper", |c| {
        if c.args.len() != 1 {
            bail!("upper() takes one argument");
        }
        if let Value::String(s) = &c.args[0] {
            Ok(s.to_uppercase().into())
        } else {
            bail!("upper() takes a string as the first argument");
        }
    });

    env.add_function("lower", |c| {
        if c.args.len() != 1 {
            bail!("lower() takes one argument");
        }
        if let Value::String(s) = &c.args[0] {
            Ok(s.to_lowercase().into())
        } else {
            bail!("lower() takes a string as the first argument");
        }
    });

    env.add_function("split", |c| {
        if let (Value::String(s), Value::String(sep), None) = (&c.args[0], &c.args[1], c.args.get(2)) {
            Ok(s.split(sep).map(Value::from).collect::<Vec<_>>().into())
        } else if let (Value::String(s), Value::String(sep), Some(Value::Number(n))) = (&c.args[0], &c.args[1], c.args.get(2)) {
            Ok(s.splitn(*n as usize, sep).map(Value::from).collect::<Vec<_>>().into())
        } else {
            bail!("split() takes a string as the first argument and a string as the second argument");
        }
    });

    env.add_function("splitAfter", |c| {
        if let (Value::String(s), Value::String(sep), None) = (&c.args[0], &c.args[1], c.args.get(2)) {
            Ok(s.split_inclusive(sep).map(Value::from).collect::<Vec<_>>().into())
        } else if let (Value::String(s), Value::String(sep), Some(Value::Number(n))) = (&c.args[0], &c.args[1], c.args.get(2)) {
            let mut arr = s.split_inclusive(sep).take(*n as usize - 1).map(|s| s.to_string()).collect::<Vec<_>>();
            arr.push(s.split_inclusive(sep).skip(*n as usize - 1).collect::<Vec<_>>().join(""));
            Ok(arr.into())
        } else {
            bail!("splitAfter() takes a string as the first argument and a string as the second argument");
        }
    });

    env.add_function("replace", |c| {
        if let (Value::String(s), Value::String(from), Value::String(to)) =
            (&c.args[0], &c.args[1], &c.args[2])
        {
            Ok(s.replace(from, to).into())
        } else {
            bail!("replace() takes a string as the first argument and two strings to replace");
        }
    });

    env.add_function("repeat", |c| {
        if let (Value::String(s), Value::Number(n)) = (&c.args[0], &c.args[1]) {
            Ok(s.repeat(*n as usize + 1).into())
        } else {
            bail!("repeat() takes a string as the first argument and a number as the second argument");
        }
    });

    env.add_function("indexOf", |c| {
        if let (Value::String(s), Value::String(sub)) = (&c.args[0], &c.args[1]) {
            Ok(s.find(sub).map(|i| i as i64).unwrap_or(-1).into())
        } else {
            bail!("indexOf() takes a string as the first argument and a string to search for as the second argument");
        }
    });

    env.add_function("lastIndexOf", |c| {
        if let (Value::String(s), Value::String(sub)) = (&c.args[0], &c.args[1]) {
            Ok(s.rfind(sub).map(|i| i as i64).unwrap_or(-1).into())
        } else {
            bail!("lastIndexOf() takes a string as the first argument and a string to search for as the second argument");
        }
    });

    env.add_function("hasPrefix", |c| {
        if let (Value::String(s), Value::String(prefix)) = (&c.args[0], &c.args[1]) {
            Ok(s.starts_with(prefix).into())
        } else {
            bail!("hasPrefix() takes a string as the first argument and a string to search for as the second argument");
        }
    });

    env.add_function("hasSuffix", |c| {
        if let (Value::String(s), Value::String(suffix)) = (&c.args[0], &c.args[1]) {
            Ok(s.ends_with(suffix).into())
        } else {
            bail!("hasSuffix() takes a string as the first argument and a string to search for as the second argument");
        }
    });
}