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
101
102
103
104
105
106
107
108
109
110
111
112
extern crate proc_macro;

#[macro_use]
extern crate nom;
#[macro_use]
extern crate quote;

mod generator;
mod logger;
mod parser;

use proc_macro::TokenStream;
use syn;

use std::{
    collections::BTreeMap,
    fs,
    path::{Path, PathBuf},
};

use yarte_config::{read_config_file, Config};

use crate::generator::{visit_derive, Print};
use crate::logger::log;
use crate::parser::{parse, parse_partials, Node};

#[proc_macro_derive(Template, attributes(template))]
pub fn derive_template(input: TokenStream) -> TokenStream {
    let i: syn::DeriveInput = syn::parse(input).unwrap();
    build_template(&i).parse().unwrap()
}

fn build_template(i: &syn::DeriveInput) -> String {
    let config_toml: &str = &read_config_file();
    let config = &Config::new(config_toml);

    let s = visit_derive(i, &config);

    let mut sources = BTreeMap::new();

    let mut check = vec![(s.path.clone(), s.source.clone())];
    while let Some((path, src)) = check.pop() {
        for n in &parse_partials(&src) {
            match n {
                Node::Partial(_, partial) => {
                    let extends = config.find_template(
                        append_extension(&s.path, partial).to_str().unwrap(),
                        Some(&path),
                    );
                    let source = get_source(&extends);
                    check.push((extends, source));
                }
                _ => unreachable!(),
            }
        }
        sources.insert(path, src);
    }

    let mut parsed = BTreeMap::new();
    for (p, src) in &sources {
        parsed.insert(p, parse(src));
    }

    if s.print == Print::Ast || s.print == Print::All {
        eprintln!("{:?}\n", parsed);
    }

    let code = generator::generate(&s, &parsed);
    if s.print == Print::Code || s.print == Print::All {
        // TODO: add theme config.print.theme or anything
        log(&code, s.path.to_str().unwrap().to_owned(), None);
    }

    code
}

fn append_extension(parent: &PathBuf, path: &str) -> PathBuf {
    let p = PathBuf::from(path);
    if p.extension().is_some() {
        p
    } else {
        if let Some(ext) = parent.extension() {
            p.with_extension(ext)
        } else {
            p
        }
    }
}

pub(crate) fn get_source(path: &Path) -> String {
    match fs::read_to_string(path) {
        Err(_) => panic!("unable to open template file '{:?}'", path),
        Ok(mut source) => match source
            .as_bytes()
            .iter()
            .enumerate()
            .rev()
            .find_map(|(j, x)| {
                if x.is_ascii_whitespace() {
                    None
                } else {
                    Some(j)
                }
            }) {
            Some(j) => {
                source.drain(j + 1..);
                source
            }
            None => source,
        },
    }
}