fgen/
generator.rs

1use tera::Tera;
2use tera::Context;
3
4use std::path::{Path, PathBuf};
5use std::io::prelude::*;
6use std::fs::OpenOptions;
7use std::fs;
8
9use errors::*;
10
11#[derive(Debug)]
12pub struct Generator {
13    project_path: PathBuf,
14    template_path: PathBuf,
15    tera: Tera,
16}
17
18impl Generator {
19    pub fn new(project_path: &Path, template_path: &Path) -> Result<Generator> {
20        if !project_path.exists() {
21            return Err(ErrorKind::ProjectDirErr(
22                project_path
23                    .to_str()
24                    .unwrap_or("Project path error")
25                    .to_owned(),
26            ).into());
27        }
28
29        if !template_path.exists() {
30            return Err(ErrorKind::TemplateDirErr(
31                template_path
32                    .to_str()
33                    .unwrap_or("Template path error")
34                    .to_owned(),
35            ).into());
36        }
37
38        let template_pattern = template_path.join("**/*");
39        let template_pattern_str = template_pattern.to_str().unwrap();
40
41        let tera = compile_templates!(template_pattern_str);
42
43        Ok(Generator {
44            project_path: project_path.to_path_buf(),
45            template_path: template_path.to_path_buf(),
46            tera: tera,
47        })
48    }
49
50    pub fn get_template_path(&self) -> &Path {
51        &self.template_path
52    }
53
54    pub fn get_project_path(&self) -> &Path {
55        &self.project_path
56    }
57
58    pub fn generate_file(&self, context: &Context, src_path: &Path, dst_path: &Path) -> Result<()> {
59        let src_path_str = src_path.to_str().ok_or("Invalid src path")?;
60        let dst_path_str = dst_path.to_str().ok_or("Invalid dst path")?;
61
62        if src_path_str.is_empty() {
63            error!("Invalid src path {} ", src_path_str);
64            return Err(ErrorKind::SrcPathErr(src_path_str.to_owned()).into());
65        }
66
67        if dst_path_str.is_empty() {
68            error!("Generating {} ...", dst_path_str);
69            return Err(ErrorKind::DstPathErr(dst_path_str.to_owned()).into());
70        }
71
72        let mut dst_exists = self.project_path.clone();
73        dst_exists.push(dst_path);
74
75        if !dst_exists.parent().unwrap().exists() {
76            info!(
77                "Create dirs {} ",
78                dst_exists.parent().unwrap().to_str().unwrap()
79            );
80            fs::create_dir(dst_exists.parent().unwrap()).chain_err(|| "Failed to create dirs")?;
81        }
82
83        let result = self.tera
84            .render(src_path_str, &context)
85            .chain_err(|| format!("Failed to render file {}", src_path_str))?;
86
87        let dst_path_string = match dst_path_str.is_empty() {
88            true => self.project_path.join(src_path),
89            false => self.project_path.join(dst_path),
90        };
91
92        let mut file = OpenOptions::new()
93            .read(true)
94            .write(true)
95            .create(true)
96            .open(dst_path_string)
97            .chain_err(|| "Failed to open file")?;
98
99        file.write_all(result.as_bytes())?;
100        info!("Template {} rendered into {} ", src_path_str, dst_path_str);
101
102        Ok(())
103    }
104}
105
106#[cfg(test)]
107mod tests {
108
109    extern crate tempdir;
110
111    use super::*;
112    use std::fs::File;
113
114    #[test]
115    fn initialization_ok() {
116        let dir = tempdir::TempDir::new("initialization").unwrap();
117        let project_path = dir.path();
118        let template_path = Path::new("samples");
119
120        let generator = Generator::new(project_path, template_path).unwrap();
121
122        assert_eq!(project_path, generator.get_project_path());
123        assert_eq!(template_path, generator.get_template_path());
124    }
125
126    #[test]
127    fn missing_project_dir() {
128        let project_path = Path::new("none");
129        let template_path = Path::new("samples");
130
131        let generator_err = Generator::new(project_path, template_path).err().unwrap();
132
133        match generator_err.kind() {
134            &ErrorKind::ProjectDirErr(_) => assert!(true),
135            &_ => assert!(false, "Expected ProjectDirErr"),
136        }
137    }
138
139    #[test]
140    fn missing_template_dir() {
141        let dir = tempdir::TempDir::new("initialization").unwrap();
142        let project_path = dir.path();
143        let template_path = Path::new("none");
144
145        let generator_err = Generator::new(project_path, template_path).err().unwrap();
146
147        match generator_err.kind() {
148            &ErrorKind::TemplateDirErr(_) => assert!(true),
149            &_ => assert!(false, "Expected TemplateDirErr"),
150        }
151    }
152
153    #[test]
154    fn missing_src_path() {
155        let dir = tempdir::TempDir::new("doesnt_exist").unwrap();
156        let project_path = dir.path();
157        let template_path = Path::new("samples");
158        let src_path = Path::new("");
159        let dst_path = Path::new("dst/file.b");
160
161        let context = Context::new();
162
163        let generator = Generator::new(project_path, template_path).unwrap();
164
165        let err = generator
166            .generate_file(&context, src_path, dst_path)
167            .err()
168            .unwrap();
169
170        match err.kind() {
171            &ErrorKind::SrcPathErr(_) => (),
172            &_ => assert!(false, "Expected SrcPathErr"),
173        }
174    }
175
176    #[test]
177    fn missing_dst_path() {
178        let dir = tempdir::TempDir::new("doesnt_exist").unwrap();
179        let project_path = dir.path();
180        let template_path = Path::new("samples");
181        let src_path = Path::new("src/file.a");
182        let dst_path = Path::new("");
183
184        let context = Context::new();
185
186        let generator = Generator::new(project_path, template_path).unwrap();
187
188        let err = generator
189            .generate_file(&context, src_path, dst_path)
190            .err()
191            .unwrap();
192
193        match err.kind() {
194            &ErrorKind::DstPathErr(_) => (),
195            &_ => assert!(false),
196        }
197    }
198
199    #[test]
200    fn render_ok() {
201        let dir = tempdir::TempDir::new("render_ok").unwrap();
202        let project_path = dir.path();
203        let template_path = Path::new("samples");
204
205        let src_path = Path::new("render_ok.txt");
206        let dst_path = Path::new("samples_ok.txt");
207
208        let generator = Generator::new(project_path, template_path).unwrap();
209
210        let mut context = Context::new();
211        context.add("msg", &"Hello World!");
212
213        generator
214            .generate_file(&context, src_path, dst_path)
215            .unwrap();
216
217        let mut file = File::open(dir.path().join("samples_ok.txt")).unwrap();
218        let mut content = String::new();
219
220        file.read_to_string(&mut content).unwrap();
221
222        assert_eq!(content, "Hello World!\n");
223    }
224
225    #[test]
226    fn render_nested_ok() {
227        let dir = tempdir::TempDir::new("render_ok").unwrap();
228        let project_path = dir.path();
229        let template_path = Path::new("samples");
230
231        let src_path = Path::new("render_ok.txt");
232        let dst_path = Path::new("nested/samples_ok.txt");
233
234        let generator = Generator::new(project_path, template_path).unwrap();
235
236        let mut context = Context::new();
237        context.add("msg", &"Hello World!");
238
239        generator
240            .generate_file(&context, src_path, dst_path)
241            .unwrap();
242
243        let mut file = File::open(dir.path().join("nested/samples_ok.txt")).unwrap();
244        let mut content = String::new();
245
246        file.read_to_string(&mut content).unwrap();
247
248        assert_eq!(content, "Hello World!\n");
249    }
250}