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}