handbar 0.0.5

Fork of Handlebars.
use serde_json::value::Value as Json;

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::ScopedJson;
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

#[derive(Clone, Copy)]
pub struct InsertHelper {
    name: crate::HelperName,
}

impl HelperDef for InsertHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'rc>,
        _: &'reg Registry<'reg>,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
    ) -> Result<ScopedJson<'rc>, RenderError> {
        let collection = h
            .param(0)
            .ok_or_else(|| {
                RenderError::new(format!(
                    "Param not found for helper \"{}\": collection",
                    self.name
                ))
            })?
            .value();

        let position = h
            .param(1)
            .ok_or_else(|| {
                RenderError::new(format!(
                    "Param not found for helper \"{}\": position",
                    self.name
                ))
            })?
            .value();

        let item = h
            .param(2)
            .ok_or_else(|| {
                RenderError::new(format!(
                    "Param not found for helper \"{}\": item",
                    self.name
                ))
            })?
            .value();

        match (collection, position) {
            (Json::Array(list), Json::Number(index)) => {
                let index = index.as_u64().ok_or_else(|| {
                    RenderError::new(format!(
                        "Param invalid for helper \"{}\": position must be positive number",
                        self.name
                    ))
                })? as usize;

                if index > list.len() {
                    return Err(RenderError::new(format!(
                        "Param invalid for helper \"{}\": position out of range",
                        self.name
                    )))
                }

                let mut result = list.clone();
                result.insert(index, item.to_owned());
                Ok(Json::Array(result).into())
            }
            (Json::Object(map), Json::String(key)) => {
                let mut result = map.clone();
                result.insert(key.clone(), item.to_owned());
                Ok(Json::Object(result).into())
            }
            _ => Err(RenderError::new(format!(
                "Param invalid for helper \"{}\": unsupported insertion tuple (collection: {:?}, position: {:?})",
                self.name,
                collection,
                position,
            ))),
        }
    }
}

pub static INSERT_HELPER: InsertHelper = InsertHelper {
    name: crate::HelperName::Insert,
};

#[cfg(test)]
mod test {
    #[test]
    fn test_insert() {
        assert_renders![
            (
                r##"{{ json (insert {} "foo" "foo") }}"##,
                r##"{"foo":"foo"}"##
            ),
            (
                r##"{{ json (insert [1, 2] 0 "foo") }}"##,
                r##"["foo",1,2]"##
            ),
            (
                r##"{{ json (insert [1, 2] 2 "foo") }}"##,
                r##"[1,2,"foo"]"##
            ),
        ];
    }
}