1mod document;
35pub mod options;
36
37pub use document::Document;
38pub use document::Token;
39pub use options::Options;
40
41#[macro_export]
42macro_rules! variables {
43 ($template:expr, $variable:ident) => {
44 $template.set(stringify!($variable), $variable);
45 };
46
47 ($template:expr, $variable:ident, $($variables:ident),+) => {
48 variables!($template, $variable);
49 variables!($template, $($variables),+)
50 }
51}
52
53#[macro_export]
54macro_rules! set {
55 ($template:expr, $key:ident, $($arg:tt)*) => {
56 $template.set(stringify!($key), std::fmt::format(format_args!($($arg)*)));
57 };
58}
59
60#[cfg(test)]
61mod test {
62 use crate::options::IncludeMethod;
63
64 use super::*;
65 use std::path::PathBuf;
66
67 #[test]
68 fn compile_all_set() {
69 let mut doc = Document::from_str(
70 "One: {one} | Two: {two} | Three: {three}",
71 Options::default(),
72 )
73 .unwrap();
74 doc.set("one", "1");
75 doc.set("two", "2");
76 doc.set("three", "3");
77
78 assert_eq!(&doc.compile(), "One: 1 | Two: 2 | Three: 3");
79 }
80
81 #[test]
82 fn compile_some_set() {
83 let mut doc = Document::from_str(
84 "One: {one} | Two: {two} | Three: {three}",
85 Options::default(),
86 )
87 .unwrap();
88 doc.set("one", "1");
89 doc.set("three", "3");
90
91 assert_eq!(&doc.compile(), "One: 1 | Two: {two} | Three: 3");
92 }
93
94 #[test]
97 fn no_text() {
98 let doc = Document::from_str("", Options::default()).unwrap();
99 assert_eq!(doc.tokens, vec![]);
100 }
101
102 #[test]
103 fn only_text() {
104 let doc = Document::from_str("Nothing but text", Options::default()).unwrap();
105 assert_eq!(
106 doc.tokens,
107 vec![Token::Text(String::from("Nothing but text"))]
108 );
109 }
110
111 #[test]
112 fn escaped_bracket() {
113 let doc =
114 Document::from_str("escape this: \\{, but not this \\n", Options::default()).unwrap();
115 assert_eq!(
116 doc.tokens,
117 vec![Token::Text(String::from(
118 "escape this: {, but not this \\n"
119 ))]
120 );
121 }
122
123 #[test]
124 fn only_variable() {
125 let doc = Document::from_str("{variable}", Options::default()).unwrap();
126 assert_eq!(
127 doc.tokens,
128 vec![Token::Variable {
129 name: String::from("variable")
130 }]
131 );
132 }
133
134 #[test]
135 fn sandwhiched_variable() {
136 let doc = Document::from_str("Hello {name}, how are you?", Options::default()).unwrap();
137 assert_eq!(
138 doc.tokens,
139 vec![
140 Token::Text(String::from("Hello ")),
141 Token::Variable {
142 name: String::from("name")
143 },
144 Token::Text(String::from(", how are you?"))
145 ]
146 );
147 }
148
149 #[test]
150 fn ends_variable() {
151 let doc = Document::from_str("Hello {name}", Options::default()).unwrap();
152 assert_eq!(
153 doc.tokens,
154 vec![
155 Token::Text(String::from("Hello ")),
156 Token::Variable {
157 name: String::from("name")
158 }
159 ]
160 );
161 }
162
163 #[test]
164 fn starts_variable() {
165 let doc = Document::from_str("{name}, hello!", Options::default()).unwrap();
166 assert_eq!(
167 doc.tokens,
168 vec![
169 Token::Variable {
170 name: String::from("name")
171 },
172 Token::Text(String::from(", hello!"))
173 ]
174 );
175 }
176
177 #[test]
178 fn multivariable() {
179 let doc = Document::from_str(
180 "The weather is {weather} in {location} today.",
181 Options::default(),
182 )
183 .unwrap();
184 assert_eq!(
185 doc.tokens,
186 vec![
187 Token::Text(String::from("The weather is ")),
188 Token::Variable {
189 name: String::from("weather")
190 },
191 Token::Text(String::from(" in ")),
192 Token::Variable {
193 name: String::from("location")
194 },
195 Token::Text(String::from(" today."))
196 ]
197 );
198 }
199
200 #[test]
201 fn include_test() {
202 let doc = Document::from_file("test/include_test.bpl", Options::default()).unwrap();
203 assert_eq!(
204 doc.tokens,
205 vec![
206 Token::Text("Before the include!\n".into()),
207 Token::Text("The included file! With a ".into()),
208 Token::Variable {
209 name: "variable".into()
210 },
211 Token::Text("!".into()),
212 Token::Text("\naand after~".into())
213 ]
214 )
215 }
216
217 #[test]
218 fn include_method_path_test() {
219 let doc = Document::from_file(
220 "test/include_some.bpl",
221 Options::default().include_path(IncludeMethod::Path(PathBuf::from("test/subdir"))),
222 )
223 .unwrap();
224 assert_eq!(
225 doc.tokens,
226 vec![
227 Token::Text("Testing IncludeMethod::Path here...\n".into()),
228 Token::Text("I'm in a subdir :D\n".into()),
229 Token::Variable {
230 name: "variable".into()
231 },
232 Token::Text("!".into())
233 ]
234 )
235 }
236
237 #[test]
238 fn complex_include() {
239 let doc =
240 Document::from_file("test/pattern_include_ifset_base.bpl", Options::default()).unwrap();
241 assert_eq!(
242 doc.tokens,
243 vec![Token::Pattern {
244 pattern_name: String::from("name"),
245 tokens: vec![Token::IfSet {
246 variable_name: String::from("variable"),
247 tokens: vec![Token::Variable {
248 name: String::from("variable")
249 }],
250 else_tokens: None
251 }]
252 }]
253 )
254 }
255
256 #[test]
257 fn ifset_variable_set() {
258 let mut doc = Document::from_str("{%if-set foo}set!{%end}", Options::default()).unwrap();
259 doc.set("foo", "bar");
260
261 assert_eq!(doc.compile(), "set!")
262 }
263
264 #[test]
265 fn ifset_variable_set_empty_string() {
266 let mut doc = Document::from_str(
267 "{%if-set foo}set!{%end}{%if-set bar}barset!{%end}",
268 Options::default(),
269 )
270 .unwrap();
271 doc.set("foo", "");
272 doc.set("bar", "set!");
273
274 assert_eq!(doc.compile(), "barset!")
275 }
276
277 #[test]
278 fn iftest_else() {
279 let doc = Document::from_str(
280 "{%if-set donotset}wasset{%else}notset{%end}",
281 Options::default(),
282 )
283 .unwrap();
284
285 assert_eq!(doc.compile(), "notset");
286 }
287
288 #[test]
289 fn pattern_parse() {
290 let doc = Document::from_str("{%pattern name}blah{variable}lah{%end}", Options::default())
291 .unwrap();
292
293 assert_eq!(
294 doc.get_pattern("name").unwrap().tokens,
295 vec![
296 Token::Text(String::from("blah")),
297 Token::Variable {
298 name: String::from("variable")
299 },
300 Token::Text(String::from("lah"))
301 ]
302 )
303 }
304
305 #[test]
306 fn pattern_fill() {
307 let mut doc =
308 Document::from_str("{%pattern name}-{variable}-{%end}", Options::default()).unwrap();
309
310 let mut pat = doc.get_pattern("name").unwrap();
311
312 let mut name = pat.clone();
313 name.set("variable", "one");
314 pat.set("variable", "two");
315
316 doc.set_pattern("name", name);
317 doc.set_pattern("name", pat);
318
319 assert_eq!(doc.compile(), String::from("-one--two-"))
320 }
321
322 #[test]
323 fn nested_scoped_commands() {
324 let doc = Document::from_str(
325 "{%pattern name}{%if-set var}{%end}{%end}",
326 Options::default(),
327 )
328 .unwrap();
329 assert_eq!(
330 doc.tokens,
331 vec![Token::Pattern {
332 pattern_name: String::from("name"),
333 tokens: vec![Token::IfSet {
334 variable_name: String::from("var"),
335 tokens: vec![],
336 else_tokens: None
337 }]
338 }]
339 )
340 }
341
342 #[test]
343 fn variables_macro() {
344 let mut doc = Document::from_str("{foo} and {bar}", Options::default()).unwrap();
345 let foo = "one";
346 let bar = "two";
347
348 variables!(doc, foo, bar);
349
350 assert_eq!(doc.compile(), String::from("one and two"))
351 }
352
353 #[test]
354 fn set_macro() {
355 let mut doc = Document::from_str("{foo}", Options::default()).unwrap();
356
357 set!(doc, foo, "{:X}", 17);
358
359 assert_eq!(doc.compile(), String::from("11"))
360 }
361
362 #[test]
363 fn wrapping_include() {
364 let expected = "<html><head>Foo<title>Test!</title></head></html>";
365 let mut doc = Document::from_file("test/wrapped_include.bpl", Options::default()).unwrap();
366 doc.set("var_in", "Foo");
367
368 assert_eq!(doc.compile(), expected)
369 }
370}