1use std::collections::HashMap;
50use std::fmt;
51use std::ops::{Deref,DerefMut};
52
53
54#[derive(Clone,PartialEq,Debug)]
65pub struct Template<'a>(Pieces<'a>);
66
67impl<'a> Deref for Template<'a> {
68 type Target = Vec<Piece<'a>>;
69 fn deref(&self) -> &Self::Target {
70 &(self.0).0
71 }
72}
73
74impl<'a> DerefMut for Template<'a> {
75 fn deref_mut(&mut self) -> &mut Self::Target {
76 &mut (self.0).0
77 }
78}
79
80impl<'a> fmt::Display for Template<'a> {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 self.0.fmt(f)
83 }
84}
85
86impl<'a> Template<'a> {
87 pub fn with_pieces(v: Vec<Piece<'a>>) -> Self {
88 Template(Pieces(v))
89 }
90
91 pub fn fill_in(&'a self, lookup_tbl: &HashMap<&'a str, &'a str>) -> Text<'a> {
94 let mut t = Text::new();
95
96 for v in self.0.iter() {
97 match v {
98 Piece::Text(s) => t.push(s),
99 Piece::Placeholder{name, before, after} => {
100 let entry = lookup_tbl.get(name);
101 match entry {
102 Some(value) if value.len() > 0 => {
103 if before.len() > 0 { t.push(before) };
104 t.push(value);
105 if after.len() > 0 { t.push(after) };
106 }
107 _ => {}
108 }
109 }
110 }
111 }
112
113 t
114 }
115
116 pub fn try_fill_in(&'a self, lookup_tbl: &HashMap<&'a str, &'a str>) -> Result<Text<'a>, TemplateError> {
119 let mut t = Text::new();
120
121 for v in self.0.iter() {
122 match v {
123 Piece::Text(s) => t.push(s),
124 Piece::Placeholder{name, before, after} => {
125 let entry = lookup_tbl.get(name);
126 match entry {
127 Some(value) => {
128 if before.len() > 0 { t.push(before) };
129 t.push(value);
130 if after.len() > 0 { t.push(after) };
131 },
132 None => { return Err(TemplateError) }
133 }
134 }
135 }
136 }
137
138 Ok(t)
139 }
140
141 }
145
146impl<'a> From<&'a str> for Template<'a> {
147 fn from(s: &'a str) -> Self {
148 enum State{InText, InPlaceholder};
149
150 let mut v: Vec<Piece> = vec![];
151 let mut state = State::InText;
152 let mut rest = s;
153
154 while rest.len() > 0 {
155 match state {
156 State::InText => {
157 if let Some(idx) = rest.find("${") {
158 v.push(Piece::Text(&rest[..idx]));
159 rest = &rest[idx+2..];
160 state = State::InPlaceholder;
161 } else {
162 v.push(Piece::Text(rest));
163 rest = &rest[0..0];
164 }
165 }
166 State::InPlaceholder => {
167 if let Some(idx) = rest.find("}") {
168 let (before, name, after) = trim_split(&rest[..idx]);
169 v.push(Piece::Placeholder{name, before, after});
170
171 rest = &rest[idx+1..];
172 state = State::InText;
173 } else {
174 v.push(Piece::Text(rest));
175 rest = &rest[0..0];
176 }
177 }
178 }
179 }
180 Template::with_pieces(v)
181 }
182}
183
184
185
186#[derive(Clone,PartialEq,Debug)]
188struct Pieces<'a>(Vec<Piece<'a>>);
189
190impl<'a> Pieces<'a> {
191}
192
193impl<'a> Deref for Pieces<'a> {
194 type Target = Vec<Piece<'a>>;
195 fn deref(&self) -> &Self::Target {
196 &self.0
197 }
198}
199
200impl<'a> fmt::Display for Pieces<'a> {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 self.0.iter().map(|v| v.fmt(f) ).collect()
203 }
204}
205
206#[derive(Clone,PartialEq,Debug)]
208pub enum Piece<'a> {
209 Text(&'a str),
210 Placeholder{name: &'a str, before: &'a str, after: &'a str}
211}
212
213impl<'a> fmt::Display for Piece<'a> {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215 match self {
216 Piece::Text(s) => { write!(f, "{}", s) },
217 Piece::Placeholder{name, before, after} => {
218 write!(f, "${{{}{}{}}}", before, name, after)
219 }
220 }
221 }
222}
223
224
225#[derive(Debug)]
229pub struct Text<'a>(Vec<&'a str>);
230
231impl<'a> Deref for Text<'a> {
232 type Target = Vec<&'a str>;
233 fn deref(&self) -> &Self::Target {
234 &self.0
235 }
236}
237
238impl<'a> DerefMut for Text<'a> {
239 fn deref_mut(&mut self) -> &mut Self::Target {
240 &mut self.0
241 }
242}
243
244impl<'a> Text<'a> {
245 fn new() -> Self {
246 Text(Vec::new())
247 }
248}
249
250impl<'a> fmt::Display for Text<'a> {
251 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252 self.0.iter().map(|v| v.fmt(f) ).collect()
253 }
254}
255
256#[derive(Debug)]
257pub struct TemplateError;
259
260fn trim_split
262(s: &str) -> (&str, &str, &str) {
263 let mut name = s;
264 let before = if let Some((last,_)) = s.chars().enumerate().take_while(|(_,v)| v.is_whitespace()).last() {
265 name = &name[last+1..];
266 &s[..last+1]
267 } else {
268 ""
269 };
270
271 let after = if let Some((first,_)) = name.chars().rev().enumerate().take_while(|(_,v)| v.is_whitespace()).last() {
272 let res = &name[name.len() - first - 1..];
273 name = &name[..name.len() - first - 1];
274 res
275 } else {
276 ""
277 };
278
279 (before, name, after)
280}
281
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286 use std::collections::HashMap;
287
288
289
290 #[test]
291 fn split() {
292 assert_eq!(trim_split("Hallo"), ("", "Hallo", ""));
293 assert_eq!(trim_split(" Hallo"), (" ", "Hallo", ""));
294 assert_eq!(trim_split("Hallo "), ("", "Hallo", " "));
295 assert_eq!(trim_split(" Hallo "), (" ", "Hallo", " "));
296 }
297
298 #[test]
299 fn from() {
300 assert_eq!(*Template::from(""), vec![]);
301 assert_eq!(*Template::from("{"), vec![Piece::Text("{")]);
302 assert_eq!(*Template::from("}"), vec![Piece::Text("}")]);
303 }
304
305 #[test]
306 fn to_string() {
307 assert_eq!(Template::from("").to_string(), "");
308 assert_eq!(Template::from("{").to_string(), "{");
309 assert_eq!(Template::from("}").to_string(), "}");
310 assert_eq!(Template::from("${}").to_string(), "${}");
311 assert_eq!(Template::from("${x}").to_string(), "${x}");
312 assert_eq!(Template::from(" ${x}").to_string(), " ${x}");
313 assert_eq!(Template::from("${x} ").to_string(), "${x} ");
314 assert_eq!(Template::from("${x }").to_string(), "${x }");
315 assert_eq!(Template::from("${ x}").to_string(), "${ x}");
316 assert_eq!(Template::from("${ x }").to_string(), "${ x }");
317 assert_eq!(Template::from("Hallo ${name}").to_string(), "Hallo ${name}");
318 }
319
320 #[test]
321 fn fill_in() {
322 let mut dict = HashMap::new();
323 dict.insert("k", "v");
324 dict.insert("l", "");
325
326 assert_eq!(Template::from("${}").fill_in(&dict).to_string(), "");
327 assert_eq!(Template::from("${k}").fill_in(&dict).to_string(), "v");
328 assert_eq!(Template::from("${ k }").fill_in(&dict).to_string(), " v ");
329 assert_eq!(Template::from("${l}").fill_in(&dict).to_string(), "");
330 assert_eq!(Template::from("${ l }").fill_in(&dict).to_string(), "");
331 }
332}