jslt 0.0.6

Everyones favorite xslt but for json
Documentation
use std::{borrow::Cow, collections::HashMap, fmt, sync::Arc};

use serde_json::Value;

use crate::{
  error::Result,
  transform::{Transform, expr::ExprTransformer},
};

pub(crate) mod builtins;

#[derive(Clone)]
pub enum JsltFunction {
  Static(&'static (dyn Fn(&[Value]) -> Result<Value> + Send + Sync)),
  Dynamic(DynamicFunction),
}

impl JsltFunction {
  pub fn call(&self, context: Context<'_>, input: &Value, arguments: &[Value]) -> Result<Value> {
    match self {
      JsltFunction::Static(function) => function(arguments),
      JsltFunction::Dynamic(function) => function.call(context, input, arguments),
    }
  }
}

impl fmt::Debug for JsltFunction {
  fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
    match self {
      JsltFunction::Static(_) => fmt.debug_tuple("Static").finish(),
      JsltFunction::Dynamic(function) => fmt.debug_tuple("Dynamic").field(&function).finish(),
    }
  }
}

#[derive(Debug, Clone)]
pub struct DynamicFunction {
  pub name: String,
  pub arguments: Vec<String>,
  pub expr: Arc<ExprTransformer>,
}

impl DynamicFunction {
  pub fn call(
    &self,
    mut context: Context<'_>,
    input: &Value,
    arguments: &[Value],
  ) -> Result<Value> {
    let arguments = self
      .arguments
      .iter()
      .zip(arguments)
      .map(|(name, value)| (name.clone(), value.clone()));

    context.to_mut().variables.extend(arguments);

    self.expr.transform_value(context, input)
  }
}

macro_rules! include_builtin {
  ($functions:ident, $ident:ident, $name:expr) => {
    $functions.insert($name.to_owned(), JsltFunction::Static(&builtins::$ident))
  };
  ($functions:ident, $ident:ident) => {
    include_builtin!($functions, $ident, stringify!($ident))
  };
}

#[derive(Clone, Debug)]
pub struct JsltContext {
  pub functions: HashMap<String, JsltFunction>,
  pub variables: HashMap<String, Value>,
}

impl Default for JsltContext {
  fn default() -> Self {
    let mut functions = HashMap::default();
    let variables = HashMap::default();

    include_builtin!(functions, contains);
    include_builtin!(functions, size);
    include_builtin!(functions, error);
    include_builtin!(functions, fallback);
    include_builtin!(functions, min);
    include_builtin!(functions, max);
    include_builtin!(functions, is_number, "is-number");
    include_builtin!(functions, is_integer, "is-integer");
    include_builtin!(functions, is_decimal, "is-decimal");
    include_builtin!(functions, number);
    include_builtin!(functions, round);
    include_builtin!(functions, floor);
    include_builtin!(functions, ceiling);
    include_builtin!(functions, random);
    include_builtin!(functions, sum);
    include_builtin!(functions, r#mod, "mod");
    include_builtin!(functions, hash_int, "hash-int");
    include_builtin!(functions, is_string, "is-string");
    include_builtin!(functions, string);
    include_builtin!(functions, test);
    include_builtin!(functions, capture);
    include_builtin!(functions, split);
    include_builtin!(functions, join);
    include_builtin!(functions, lowercase);
    include_builtin!(functions, uppercase);
    include_builtin!(functions, sha256_hex, "sha256-hex");
    include_builtin!(functions, starts_with, "starts-with");
    include_builtin!(functions, ends_with, "ends-with");
    include_builtin!(functions, from_json, "from-json");
    include_builtin!(functions, to_json, "to-json");
    include_builtin!(functions, replace);
    include_builtin!(functions, trim);
    include_builtin!(functions, uuid);
    include_builtin!(functions, boolean);
    include_builtin!(functions, not);
    include_builtin!(functions, is_boolean, "is-boolean");
    include_builtin!(functions, is_object, "is-object");
    include_builtin!(functions, get_key, "get-key");
    include_builtin!(functions, array);
    include_builtin!(functions, is_array, "is-array");
    include_builtin!(functions, flatten);
    include_builtin!(functions, all);
    include_builtin!(functions, any);
    include_builtin!(functions, zip);
    include_builtin!(functions, zip_with_index, "zip-with-index");
    include_builtin!(functions, index_of, "index-of");
    include_builtin!(functions, now);
    include_builtin!(functions, parse_time, "parse-time");
    include_builtin!(functions, format_time, "format-time");
    include_builtin!(functions, parse_url, "parse-url");

    JsltContext {
      functions,
      variables,
    }
  }
}

pub type Context<'s> = Cow<'s, JsltContext>;