jslt 0.0.6

Everyones favorite xslt but for json
Documentation
use std::{fmt, fmt::Write};

use jslt_macro::expect_inner;
use pest::iterators::Pairs;
use serde_json::Value;

use crate::{
  context::Context,
  error::{JsltError, Result},
  format,
  parser::{FromPairs, Rule},
  transform::{
    Transform,
    expr::ExprTransformer,
    value::{accessor::AccessorTransformer, array::ArrayTransformer, object::ObjectTransformer},
  },
};

pub mod accessor;
pub mod array;
pub mod object;

#[derive(Debug)]
pub struct BooleanTransformer(bool);

impl FromPairs for BooleanTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    let pair = pairs.next().ok_or(JsltError::UnexpectedEnd)?;

    let rule = pair.as_rule();

    if matches!(rule, Rule::Boolean) {
      Ok(BooleanTransformer(
        pair
          .as_str()
          .parse()
          .map_err(|_| JsltError::UnexpectedContent(Rule::Boolean))?,
      ))
    } else {
      Err(JsltError::UnexpectedInput(
        Rule::Boolean,
        rule,
        pair.as_str().to_owned(),
      ))
    }
  }
}

impl Transform for BooleanTransformer {
  fn transform_value(&self, _: Context<'_>, _: &Value) -> Result<Value> {
    Ok(Value::Bool(self.0))
  }
}

impl format::Display for BooleanTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    let BooleanTransformer(value) = self;
    write!(f, "{value}")
  }
}

#[derive(Debug)]
pub struct NullTransformer;

impl Transform for NullTransformer {
  fn transform_value(&self, _: Context<'_>, _: &Value) -> Result<Value> {
    Ok(Value::Null)
  }
}

impl format::Display for NullTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    write!(f, "null")
  }
}

#[derive(Debug)]
pub struct NumberTransformer(serde_json::Number);

impl FromPairs for NumberTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    let pair = pairs.next().ok_or(JsltError::UnexpectedEnd)?;

    let rule = pair.as_rule();

    if matches!(rule, Rule::Number) {
      Ok(NumberTransformer(
        pair
          .as_str()
          .parse()
          .map_err(|_| JsltError::UnexpectedContent(Rule::Number))?,
      ))
    } else {
      Err(JsltError::UnexpectedInput(
        Rule::Number,
        rule,
        pair.as_str().to_owned(),
      ))
    }
  }
}

impl Transform for NumberTransformer {
  fn transform_value(&self, _: Context<'_>, _: &Value) -> Result<Value> {
    Ok(Value::Number(self.0.clone()))
  }
}

impl format::Display for NumberTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    let NumberTransformer(value) = self;
    write!(f, "{value}")
  }
}

#[derive(Debug)]
pub struct ScopeTransformer(Box<ExprTransformer>);

impl FromPairs for ScopeTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    let mut pairs = expect_inner!(pairs, Rule::Scope)?;

    ExprTransformer::from_pairs(&mut pairs)
      .map(Box::new)
      .map(ScopeTransformer)
  }
}

impl Transform for ScopeTransformer {
  fn transform_value(&self, context: Context<'_>, input: &Value) -> Result<Value> {
    self.0.transform_value(context, input)
  }
}

impl format::Display for ScopeTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    let ScopeTransformer(expr) = self;
    f.write_char('(')?;
    format::Display::fmt(expr.as_ref(), f)?;
    f.write_char(')')
  }
}

#[derive(Debug)]
pub struct StringTransformer(String);

impl FromPairs for StringTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    let mut pairs = expect_inner!(pairs, Rule::String)?;

    let inner = pairs.next().ok_or(JsltError::UnexpectedEnd)?;

    let rule = inner.as_rule();

    if matches!(rule, Rule::Inner) {
      unescaper::unescape(inner.as_str())
        .map(StringTransformer)
        .map_err(JsltError::Unescape)
    } else {
      Err(JsltError::UnexpectedInput(
        Rule::String,
        rule,
        inner.as_str().to_owned(),
      ))
    }
  }
}

impl Transform for StringTransformer {
  fn transform_value(&self, _: Context<'_>, _: &Value) -> Result<Value> {
    Ok(Value::String(self.0.clone()))
  }
}

impl format::Display for StringTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    let StringTransformer(value) = self;
    write!(f, "{value:?}")
  }
}

#[derive(Debug)]
pub enum ValueTransformer {
  Accessor(AccessorTransformer),
  Array(ArrayTransformer),
  Boolean(BooleanTransformer),
  Null(NullTransformer),
  Number(NumberTransformer),
  Object(ObjectTransformer),
  Scope(ScopeTransformer),
  String(StringTransformer),
  Variable(VariableTransformer),
}

impl FromPairs for ValueTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    if let Some(pair) = pairs.peek() {
      return match pair.as_rule() {
        Rule::Accessor => AccessorTransformer::from_pairs(pairs).map(ValueTransformer::Accessor),
        Rule::Array => ArrayTransformer::from_pairs(pairs).map(ValueTransformer::Array),
        Rule::Boolean => BooleanTransformer::from_pairs(pairs).map(ValueTransformer::Boolean),
        Rule::Number => NumberTransformer::from_pairs(pairs).map(ValueTransformer::Number),
        Rule::Object => ObjectTransformer::from_pairs(pairs).map(ValueTransformer::Object),
        Rule::Scope => ScopeTransformer::from_pairs(pairs).map(ValueTransformer::Scope),
        Rule::String => StringTransformer::from_pairs(pairs).map(ValueTransformer::String),
        Rule::Variable => VariableTransformer::from_pairs(pairs).map(ValueTransformer::Variable),
        Rule::Null => {
          let _ = pairs.next();
          Ok(ValueTransformer::Null(NullTransformer))
        }
        rule => Err(JsltError::UnexpectedInput(
          Rule::Value,
          rule,
          pair.as_str().to_owned(),
        )),
      };
    }

    Err(JsltError::UnexpectedEnd)
  }
}

impl Transform for ValueTransformer {
  fn transform_value(&self, context: Context<'_>, input: &Value) -> Result<Value> {
    match self {
      ValueTransformer::Accessor(accessor) => accessor.transform_value(context, input),
      ValueTransformer::Array(array) => array.transform_value(context, input),
      ValueTransformer::Boolean(boolean) => boolean.transform_value(context, input),
      ValueTransformer::Null(null) => null.transform_value(context, input),
      ValueTransformer::Number(number) => number.transform_value(context, input),
      ValueTransformer::Object(object) => object.transform_value(context, input),
      ValueTransformer::Scope(scope) => scope.transform_value(context, input),
      ValueTransformer::String(string) => string.transform_value(context, input),
      ValueTransformer::Variable(variable) => variable.transform_value(context, input),
    }
  }
}

impl format::Display for ValueTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    match self {
      ValueTransformer::Accessor(accessor) => {
        format::Display::fmt(&accessor::RootAccessorDisplay(accessor), f)
      }
      ValueTransformer::Array(array) => format::Display::fmt(array, f),
      ValueTransformer::Boolean(boolean) => format::Display::fmt(boolean, f),
      ValueTransformer::Null(null) => format::Display::fmt(null, f),
      ValueTransformer::Number(number) => format::Display::fmt(number, f),
      ValueTransformer::Object(object) => format::Display::fmt(object, f),
      ValueTransformer::Scope(scope) => format::Display::fmt(scope, f),
      ValueTransformer::String(string) => format::Display::fmt(string, f),
      ValueTransformer::Variable(variable) => format::Display::fmt(variable, f),
    }
  }
}

#[derive(Debug)]
pub struct VariableTransformer(String, Option<AccessorTransformer>);

impl FromPairs for VariableTransformer {
  fn from_pairs(pairs: &mut Pairs<Rule>) -> Result<Self> {
    let mut pairs = expect_inner!(pairs, Rule::Variable)?;

    let ident = pairs.next().ok_or(JsltError::UnexpectedEnd)?;

    Ok(VariableTransformer(
      ident.as_str().to_owned(),
      pairs
        .next()
        .map(|pair| AccessorTransformer::from_pairs(&mut Pairs::single(pair)))
        .transpose()?,
    ))
  }
}

impl Transform for VariableTransformer {
  fn transform_value(&self, context: Context<'_>, _: &Value) -> Result<Value> {
    let value = context.variables[&self.0].clone();

    match &self.1 {
      Some(accessor) => accessor.transform_value(context, &value),
      None => Ok(value),
    }
  }
}

impl format::Display for VariableTransformer {
  fn fmt(&self, f: &mut format::Formatter<'_>) -> fmt::Result {
    let VariableTransformer(name, accessor) = self;

    write!(f, "${name}")?;

    if let Some(accessor) = accessor {
      format::Display::fmt(accessor, f)?;
    }

    Ok(())
  }
}