aboutsummaryrefslogtreecommitdiffstats
path: root/askama_derive/src/input.rs
blob: 9ea8ddcfbad8086a21541df57e5e9b8039681dc1 (plain) (blame)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
use shared::path;

use std::path::{Path, PathBuf};

use syn;


pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub meta: TemplateMeta,
    pub path: PathBuf,
    pub source: String,
}

impl<'a> TemplateInput<'a> {
    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> {
        let meta = TemplateMeta::new(ast);
        let (path, source) = match meta.source {
            Source::Source(ref s) => {
                let path = match meta.ext {
                    Some(ref v) => PathBuf::from(format!("_.{}", v)),
                    None => PathBuf::new(),
                };
                (path, s.clone())
            },
            Source::Path(ref s) => {
                let path = path::find_template_from_path(s, None);
                let src = path::get_template_source(&path);
                (path, src)
            },
        };
        TemplateInput { ast, meta, path, source }
    }
}

// Holds metadata for the template, based on the `template()` attribute.
pub struct TemplateMeta {
    source: Source,
    pub print: Print,
    pub escaping: EscapeMode,
    pub ext: Option<String>,
}

impl TemplateMeta {
    fn new(ast: &syn::DeriveInput) -> TemplateMeta {
        let attr = ast.attrs
            .iter()
            .find(|a| a.interpret_meta().unwrap().name() == "template");
        if attr.is_none() {
            panic!(format!("'template' attribute not found on struct '{}'", ast.ident));
        }

        let attr = attr.unwrap();
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        if let syn::Meta::List(ref inner) = attr.interpret_meta().unwrap() {
            for nm_item in inner.nested.iter() {
                if let syn::NestedMeta::Meta(ref item) = *nm_item {
                    if let syn::Meta::NameValue(ref pair) = *item {
                        match pair.ident.to_string().as_ref() {
                            "path" => if let syn::Lit::Str(ref s) = pair.lit {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Path(s.value()));
                            } else {
                                panic!("template path must be string literal");
                            },
                            "source" => if let syn::Lit::Str(ref s) = pair.lit {
                                if source.is_some() {
                                    panic!("must specify 'source' or 'path', not both");
                                }
                                source = Some(Source::Source(s.value()));
                            } else {
                                panic!("template source must be string literal");
                            },
                            "print" => if let syn::Lit::Str(ref s) = pair.lit {
                                print = s.value().into();
                            } else {
                                panic!("print value must be string literal");
                            },
                            "escape" => if let syn::Lit::Str(ref s) = pair.lit {
                                escaping = Some(s.value().into());
                            } else {
                                panic!("escape value must be string literal");
                            },
                            "ext" => if let syn::Lit::Str(ref s) = pair.lit {
                                ext = Some(s.value());
                            } else {
                                panic!("ext value must be string literal");
                            },
                            attr => panic!("unsupported annotation key '{}' found", attr),
                        }
                    }
                }
            }
        }

        let source = source.expect("template path or source not found in attributes");
        match (&source, ext.is_some()) {
            (&Source::Path(_), true) => {
                panic!("'ext' attribute cannot be used with 'path' attribute")
            },
            (&Source::Source(_), false) => {
                panic!("must include 'ext' attribute when using 'source' attribute")
            },
            _ => {},
        }
        let escaping = match escaping {
            Some(m) => m,
            None => {
                let ext = match source {
                    Source::Path(ref p) =>
                        Path::new(p).extension().map(|s| s.to_str().unwrap()).unwrap_or(""),
                    Source::Source(_) => ext.as_ref().unwrap(), // Already panicked if None
                };
                if HTML_EXTENSIONS.contains(&ext) {
                    EscapeMode::Html
                } else {
                    EscapeMode::None
                }
            },
        };
        TemplateMeta { source, print, escaping, ext }
    }
}

enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum EscapeMode {
    Html,
    None,
}

impl From<String> for EscapeMode {
    fn from(s: String) -> EscapeMode {
        use self::EscapeMode::*;
        match s.as_ref() {
            "html" => Html,
            "none" => None,
            v => panic!("invalid value for escape option: {}", v),
        }
    }
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl From<String> for Print {
    fn from(s: String) -> Print {
        use self::Print::*;
        match s.as_ref() {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => panic!("invalid value for print option: {}", v),
        }
    }
}

const HTML_EXTENSIONS: [&str; 3] = ["html", "htm", "xml"];