1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
extern crate clap;
extern crate serde;

use serde::Serialize;
use std::fs::File;
use std::path::Path;

use clap::{Arg, ArgMatches};


/// Defines a mapping between a template variable, and the file to
/// populate it.
///
#[derive(Debug)]
pub struct VarMapping<'a> {
    var_name: &'a str,
    file_path: &'a str,
}

impl<'a> VarMapping<'a> {
    /// Builds a VarMapping from a String
    ///
    pub fn from_str(s: &'a str) -> Result<VarMapping<'a>, &'static str> {
        let mut splits = s.splitn(2, ":");
        let var_name = splits.next().unwrap();

        match splits.next() {
            Some(file_path) => {
                Ok(VarMapping {
                    var_name: var_name,
                    file_path: file_path,
                })
            }
            None => Err("Expected a ':' in var mapping string"),
        }
    }

    /// Builds a VarMapping from a String, and panics if it fails.
    ///
    #[inline]
    pub fn from_str_panic(s: &'a str) -> VarMapping<'a> {
        match VarMapping::from_str(s) {
            Ok(s) => s,
            Err(e) => panic!(e),
        }
    }

    #[inline]
    pub fn file_path(&self) -> &'a str {
        self.file_path
    }

    #[inline]
    pub fn var_name(&self) -> &'a str {
        self.var_name
    }
}


/// A plugin that can assign a value to a variable in the Tera context from a
/// file.
///
pub trait CompileVariablePlugin {
    /// The serializable value to add to the Tera context.
    ///
    type RenderValue: Serialize;

    /// The name of the plugin.
    ///
    fn plugin_name() -> &'static str;

    /// The name of the argument that provides the variable name to file mapping.
    ///
    fn arg_full_name() -> &'static str;

    /// The help string to display.
    ///
    fn arg_help() -> &'static str;

    /// Optional arguments to supply. These should be prefixed to avoid namespace clashes.
    ///
    fn additional_args() -> Vec<Arg<'static, 'static>>;

    /// Constructor for building the plugin from the supplied command line arguments.
    ///
    fn from_args<'a>(args: &'a ArgMatches<'a>) -> Self;

    /// Reads and parses the supplied file. The result is stored in the Tera context.
    ///
    fn read_file(&self, file: File) -> Result<Self::RenderValue, String>;

    /// Reads and parses a file at a specific path.
    ///
    fn read_path<P: AsRef<Path>>(&self, path: P) -> Result<Self::RenderValue, String> {
        match File::open(path) {
            Ok(file) => self.read_file(file),
            Err(e) => Err(format!("{:?}", e)),
        }
    }
}