dhl 0.1.2

Dependency Hijacking Library
Documentation
use std::collections::HashMap;
use std::env::{VarError, var};

use serde::{Serialize, Serializer};
use serde::ser::SerializeMap;
use handlebars::{Handlebars, TemplateRenderError};
#[cfg(feature = "rustc_version")]
use rustc_version::{Error as RustcError, version_meta};

use manifest::Substitution;


quick_error! {
    #[derive(Debug)]
    pub enum TemplateGenerationError {
        #[cfg(feature = "rustc_version")]
        RustVersionError(err: RustcError) {
            from()
            description("rustc version error")
            display("Unable to determine rustc version '{}'", err)
        }
        EnvError(err: VarError) {
            from()
            description("environment variable error")
            display("Unable to read environment variable '{}'", err)
        }
    }
}


struct Data<'a> {
    version: Option<&'a str>,
    substitutions: &'a HashMap<String, String>,
}


impl<'a> Serialize for Data<'a> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let (version, count) = match self.version {
            Some(v) => {
                if let Some(v) = self.substitutions.get("version") {
                    (Some(v.as_ref()), 0)
                } else {
                    (Some(v), 1)
                }
            }
            None => (None, 0),
        };
        let mut map = serializer.serialize_map(
            Some(self.substitutions.len() + count),
        )?;
        if let Some(version) = version {
            map.serialize_entry("version", version)?;
        }
        for (k, v) in self.substitutions {
            map.serialize_entry(k, v)?;
        }
        map.end()
    }
}


pub struct TemplateEngine {
    engine: Handlebars,
    substitutions: HashMap<String, String>,
}

impl TemplateEngine {
    pub fn new(
        substitutions: HashMap<String, Substitution>,
    ) -> Result<Self, TemplateGenerationError> {
        let mut resolved_subs = HashMap::new();
        Self::register_rustc_helpers(&mut resolved_subs)?;
        for (name, sub) in substitutions.into_iter() {
            resolved_subs.insert(
                name,
                match sub {
                    Substitution::EnvironmentVariable(key) => var(key)?,
                    Substitution::Value(val) => val,
                },
            );
        }

        Ok(TemplateEngine {
            engine: Handlebars::new(),
            substitutions: resolved_subs,
        })
    }

    #[cfg(feature = "rustc_version")]
    fn register_rustc_helpers(
        substitutions: &mut HashMap<String, String>,
    ) -> Result<(), TemplateGenerationError> {
        let version = version_meta()?;
        substitutions.insert("rustc_short_version".into(), version.short_version_string);
        Ok(())
    }

    #[cfg(not(feature = "rustc_version"))]
    fn register_rustc_helpers(substitutions: &mut HashMap<String, String>) {}

    pub fn render(
        &self,
        template: &str,
        version: Option<&str>,
    ) -> Result<String, TemplateRenderError> {
        self.engine.template_render(
            template,
            &Data {
                version,
                substitutions: &self.substitutions,
            },
        )
    }
}

#[cfg(test)]
mod test {
    use std::env::{set_var, remove_var};
    use std::collections::HashMap;

    use manifest::Substitution;
    use super::TemplateEngine;

    fn test_simple(t: &TemplateEngine) {
        assert_eq!(t.render("", None).unwrap(), "");
        assert_eq!(t.render("", Some("10".into())).unwrap(), "");

        assert_eq!(t.render("foo", None).unwrap(), "foo");
        assert_eq!(t.render("foo", Some("10".into())).unwrap(), "foo");


        assert_eq!(t.render("{{version}}", Some("10".into())).unwrap(), "10");
        assert_eq!(
            t.render("foo{{version}}", Some("10".into())).unwrap(),
            "foo10"
        );
    }

    #[test]
    fn test_version() {
        let t = TemplateEngine::new(HashMap::new()).unwrap();
        test_simple(&t);
    }

    #[test]
    fn test_value() {
        let mut map = HashMap::new();
        map.insert(
            "dhl_val".into(),
            Substitution::Value("dhl_test_value".into()),
        );

        let t = TemplateEngine::new(map).unwrap();
        test_simple(&t);
        assert_eq!(t.render("{{dhl_val}}", None).unwrap(), "dhl_test_value");
        assert_eq!(
            t.render("foo{{dhl_val}}", None).unwrap(),
            "foodhl_test_value"
        );
    }

    #[test]
    fn test_env() {
        set_var("DHL_TEST_ENV_VAR", "dhl_test_env_val");

        let mut map = HashMap::new();
        map.insert(
            "dhl_var".to_owned(),
            Substitution::EnvironmentVariable("DHL_TEST_ENV_VAR".to_owned()),
        );

        let t = TemplateEngine::new(map).unwrap();
        test_simple(&t);
        assert_eq!(t.render("{{dhl_var}}", None).unwrap(), "dhl_test_env_val");
        assert_eq!(
            t.render("foo{{dhl_var}}", None).unwrap(),
            "foodhl_test_env_val"
        );

        remove_var("DHL_TEST_ENV_VAR");
    }
}