tmpl/
lib.rs

1use serde_yaml::{Mapping, Value};
2use std::borrow::{Borrow, Cow};
3use std::collections::VecDeque;
4use std::io::Read;
5
6#[derive(Debug)]
7pub enum Error {
8    IO(std::io::Error),
9    SerDe(serde_yaml::Error),
10    UTF8(std::str::Utf8Error),
11    ParseInt(std::num::ParseIntError),
12    ParseError,
13    InvalidEscapeSequence,
14    MissingArgument,
15    MissingConfigValue(String),
16}
17impl From<std::io::Error> for Error {
18    fn from(e: std::io::Error) -> Self {
19        Error::IO(e)
20    }
21}
22impl From<std::str::Utf8Error> for Error {
23    fn from(e: std::str::Utf8Error) -> Self {
24        Error::UTF8(e)
25    }
26}
27impl From<std::num::ParseIntError> for Error {
28    fn from(e: std::num::ParseIntError) -> Self {
29        Error::ParseInt(e)
30    }
31}
32impl From<serde_yaml::Error> for Error {
33    fn from(e: serde_yaml::Error) -> Self {
34        Error::SerDe(e)
35    }
36}
37impl std::fmt::Display for Error {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        write!(f, "{:?}", self)
40    }
41}
42impl std::error::Error for Error {}
43
44#[derive(Debug, Clone)]
45pub struct EscapePattern<'a>(pub Cow<'a, [u8]>, pub Cow<'a, [u8]>);
46impl Default for EscapePattern<'static> {
47    fn default() -> Self {
48        EscapePattern(b"{{"[..].into(), b"}}"[..].into())
49    }
50}
51impl<'a> std::str::FromStr for EscapePattern<'a> {
52    type Err = Error;
53
54    fn from_str(s: &str) -> Result<Self, Self::Err> {
55        let mut split = s.split("var");
56        let res = EscapePattern(
57            split
58                .next()
59                .ok_or(Error::InvalidEscapeSequence)?
60                .as_bytes()
61                .to_owned()
62                .into(),
63            split
64                .next()
65                .ok_or(Error::InvalidEscapeSequence)?
66                .as_bytes()
67                .to_owned()
68                .into(),
69        );
70        if res.0 == res.1 {
71            Err(Error::InvalidEscapeSequence)
72        } else {
73            Ok(res)
74        }
75    }
76}
77impl<'a> std::fmt::Display for EscapePattern<'a> {
78    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79        write!(
80            f,
81            "{}var{}",
82            std::str::from_utf8(self.0.borrow()).unwrap(),
83            std::str::from_utf8(self.1.borrow()).unwrap(),
84        )
85    }
86}
87
88pub fn get_val_from_config_seq(seq: &[Value], key: &str) -> Option<Value> {
89    let mut seg_iter = key.splitn(2, ".");
90    let seg = seg_iter.next()?;
91    let val = seq.get(seg.parse::<usize>().ok()?)?;
92    match (val, seg_iter.next()) {
93        (Value::Mapping(m), Some(rest)) => get_val_from_config_map(&m, rest),
94        (Value::Sequence(s), Some(rest)) => get_val_from_config_seq(s.as_slice(), rest),
95        (Value::Null, _) => None,
96        (_, None) => Some(val.clone()),
97        _ => None,
98    }
99}
100
101pub fn get_val_from_config_map(map: &Mapping, key: &str) -> Option<Value> {
102    let mut seg_iter = key.splitn(2, ".");
103    let seg = seg_iter.next()?;
104    let val = map.get(&Value::String(seg.to_owned()))?;
105    match (val, seg_iter.next()) {
106        (Value::Mapping(m), Some(rest)) => get_val_from_config_map(&m, rest),
107        (Value::Sequence(s), Some(rest)) => get_val_from_config_seq(s.as_slice(), rest),
108        (Value::Null, _) => None,
109        (_, None) => Some(val.clone()),
110        _ => None,
111    }
112}
113
114pub fn set_val_in_config_seq(seq: &mut [Value], key: &str, value: Value) -> Option<()> {
115    let mut seg_iter = key.splitn(2, ".");
116    let seg = seg_iter.next()?;
117    let val = seq.get_mut(seg.parse::<usize>().ok()?)?;
118    match (val, seg_iter.next()) {
119        (Value::Mapping(m), Some(rest)) => set_val_in_config_map(m, rest, value),
120        (Value::Sequence(s), Some(rest)) => set_val_in_config_seq(s.as_mut_slice(), rest, value),
121        (val, None) => {
122            *val = value;
123            Some(())
124        }
125        _ => None,
126    }
127}
128
129pub fn set_val_in_config_map(map: &mut Mapping, key: &str, value: Value) -> Option<()> {
130    let mut seg_iter = key.splitn(2, ".");
131    let seg = seg_iter.next()?;
132    let val = map.get_mut(&Value::String(seg.to_owned()))?;
133    match (val, seg_iter.next()) {
134        (Value::Mapping(m), Some(rest)) => set_val_in_config_map(m, rest, value),
135        (Value::Sequence(s), Some(rest)) => set_val_in_config_seq(s.as_mut_slice(), rest, value),
136        (val, None) => {
137            *val = value;
138            Some(())
139        }
140        _ => None,
141    }
142}
143
144pub fn val_to_string(val: Value) -> String {
145    match val {
146        Value::Bool(b) => format!("{}", b),
147        Value::Mapping(_) => "{map}".to_owned(),
148        Value::Null => "null".to_owned(),
149        Value::Number(n) => format!("{}", n),
150        Value::Sequence(_) => "[list]".to_owned(),
151        Value::String(s) => s,
152    }
153}
154
155pub fn val_is_truthy(val: &Value) -> bool {
156    match val {
157        Value::Bool(false) => false,
158        Value::Null => false,
159        _ => true,
160    }
161}
162
163/// rpcpassword={{rpcpassword}}
164/// {{#IF rpcauth
165/// rpcuser={{rpcauth.rpcuser}}
166/// rpcpassword={{rpcauth.rpcpassword}}
167/// }}
168/// {{#IF listen
169/// listen=1
170/// bind=0.0.0.0:8333
171/// }}
172/// {{#IFNOT listen
173/// listen=0
174/// }}
175/// {{#FOREACH rpcallowip
176/// rpcallowip={{rpcallowip}}
177/// }}
178pub fn eval(
179    map: &Mapping,
180    expr: &str,
181    escape: &EscapePattern,
182    unescape: u8,
183) -> Result<String, Error> {
184    let trimmed = expr.trim_start();
185    if trimmed.starts_with("#IF ") {
186        let mut split = trimmed[4..].splitn(2, "\n");
187        let expr = split.next().ok_or_else(|| Error::ParseError)?.trim();
188        let rest = split.next().ok_or_else(|| Error::ParseError)?;
189        if expr.contains("!=") {
190            let mut split = expr.splitn(2, "!=");
191            let if_var = split.next().ok_or_else(|| Error::ParseError)?.trim();
192            let target = split.next().ok_or_else(|| Error::ParseError)?.trim();
193            match get_val_from_config_map(map, if_var) {
194                Some(Value::String(ref s)) => {
195                    if !target.starts_with("\"") || !target.ends_with("\"") {
196                        eprintln!("{}", target);
197                        return Err(Error::ParseError);
198                    }
199                    if format!("{:?}", s) != target {
200                        let mut ret = String::new();
201                        TemplatingReader::new(
202                            std::io::Cursor::new(rest.as_bytes()),
203                            map,
204                            escape,
205                            unescape,
206                        )
207                        .read_to_string(&mut ret)?;
208                        Ok(ret)
209                    } else {
210                        Ok("".to_owned())
211                    }
212                }
213                Some(Value::Number(n)) => {
214                    if format!("{}", n) != target {
215                        let mut ret = String::new();
216                        TemplatingReader::new(
217                            std::io::Cursor::new(rest.as_bytes()),
218                            map,
219                            escape,
220                            unescape,
221                        )
222                        .read_to_string(&mut ret)?;
223                        Ok(ret)
224                    } else {
225                        Ok("".to_owned())
226                    }
227                }
228                _ => Ok("".to_owned()),
229            }
230        } else if expr.contains("=") {
231            let mut split = expr.splitn(2, "=");
232            let if_var = split.next().ok_or_else(|| Error::ParseError)?.trim();
233            let target = split.next().ok_or_else(|| Error::ParseError)?.trim();
234            match get_val_from_config_map(map, if_var) {
235                Some(Value::String(ref s)) => {
236                    if !target.starts_with("\"") || !target.ends_with("\"") {
237                        eprintln!("{}", target);
238                        return Err(Error::ParseError);
239                    }
240                    if format!("{:?}", s) == target {
241                        let mut ret = String::new();
242                        TemplatingReader::new(
243                            std::io::Cursor::new(rest.as_bytes()),
244                            map,
245                            escape,
246                            unescape,
247                        )
248                        .read_to_string(&mut ret)?;
249                        Ok(ret)
250                    } else {
251                        Ok("".to_owned())
252                    }
253                }
254                Some(Value::Number(n)) => {
255                    if format!("{}", n) == target {
256                        let mut ret = String::new();
257                        TemplatingReader::new(
258                            std::io::Cursor::new(rest.as_bytes()),
259                            map,
260                            escape,
261                            unescape,
262                        )
263                        .read_to_string(&mut ret)?;
264                        Ok(ret)
265                    } else {
266                        Ok("".to_owned())
267                    }
268                }
269                _ => Ok("".to_owned()),
270            }
271        } else if expr.starts_with("!") {
272            let if_var = &expr[1..];
273            if !get_val_from_config_map(map, if_var)
274                .map(|a| val_is_truthy(&a))
275                .unwrap_or(false)
276            {
277                let mut ret = String::new();
278                TemplatingReader::new(std::io::Cursor::new(rest.as_bytes()), map, escape, unescape)
279                    .read_to_string(&mut ret)?;
280                Ok(ret)
281            } else {
282                Ok("".to_owned())
283            }
284        } else {
285            let if_var = expr;
286            if get_val_from_config_map(map, if_var)
287                .map(|a| val_is_truthy(&a))
288                .unwrap_or(false)
289            {
290                let mut ret = String::new();
291                TemplatingReader::new(std::io::Cursor::new(rest.as_bytes()), map, escape, unescape)
292                    .read_to_string(&mut ret)?;
293                Ok(ret)
294            } else {
295                Ok("".to_owned())
296            }
297        }
298    } else if trimmed.starts_with("#FOREACH ") {
299        let mut split = trimmed[9..].splitn(2, "\n");
300        let for_var = split.next().ok_or_else(|| Error::ParseError)?.trim();
301        let rest = split.next().ok_or_else(|| Error::ParseError)?;
302        match get_val_from_config_map(map, for_var) {
303            Some(Value::Sequence(s)) => {
304                let mut ret = String::new();
305                let mut new_map = map.clone();
306                for item in s {
307                    set_val_in_config_map(&mut new_map, for_var, item)
308                        .ok_or_else(|| Error::MissingConfigValue(for_var.to_owned()))?;
309                    TemplatingReader::new(
310                        std::io::Cursor::new(rest.as_bytes()),
311                        &new_map,
312                        escape,
313                        unescape,
314                    )
315                    .read_to_string(&mut ret)?;
316                }
317                Ok(ret)
318            }
319            Some(ref a) if val_is_truthy(a) => {
320                let mut ret = String::new();
321                TemplatingReader::new(std::io::Cursor::new(rest.as_bytes()), map, escape, unescape)
322                    .read_to_string(&mut ret)?;
323                Ok(ret)
324            }
325            _ => Ok("".to_owned()),
326        }
327    } else {
328        get_val_from_config_map(map, trimmed)
329            .map(val_to_string)
330            .ok_or_else(|| Error::MissingConfigValue(trimmed.to_owned()))
331    }
332}
333
334#[derive(Debug)]
335pub struct TemplatingReader<'a, 'b, 'c, R: Read> {
336    inner: R,
337    mapping: &'a Mapping,
338    escape: &'b EscapePattern<'c>,
339    unescape: u8,
340    unescapable: bool,
341    count_start: usize,
342    count_end: usize,
343    depth: usize,
344    var: Vec<u8>,
345    buf: VecDeque<u8>,
346}
347impl<'a, 'b, 'c, R> TemplatingReader<'a, 'b, 'c, R>
348where
349    R: Read,
350{
351    pub fn new(
352        reader: R,
353        mapping: &'a Mapping,
354        escape: &'b EscapePattern<'c>,
355        unescape: u8,
356    ) -> Self {
357        TemplatingReader {
358            inner: reader,
359            mapping,
360            escape,
361            unescape,
362            unescapable: false,
363            count_start: 0,
364            count_end: 0,
365            depth: 0,
366            var: Vec::new(),
367            buf: VecDeque::new(),
368        }
369    }
370}
371
372fn to_io_error<E: std::error::Error + Send + Sync + 'static>(e: E) -> std::io::Error {
373    std::io::Error::new(std::io::ErrorKind::Other, e)
374}
375
376impl<'a, 'b, 'c, R> Read for TemplatingReader<'a, 'b, 'c, R>
377where
378    R: Read,
379{
380    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
381        let mut written = 0;
382        while written == 0 {
383            let in_bytes = self.inner.read(buf)?;
384            for byte in &buf[..in_bytes] {
385                let mut write_byte = true;
386                let byte_arr = [];
387                let mut to_extend: &[u8] = &byte_arr;
388                if self.unescapable && byte == &self.unescape {
389                    self.depth -= 1;
390                    self.count_start = 0;
391                    to_extend = &*self.escape.0;
392                    write_byte = false;
393                }
394                self.unescapable = false;
395                if byte == &self.escape.0[self.count_start] {
396                    self.count_start += 1;
397                    if self.depth == 0 {
398                        write_byte = false;
399                    }
400                } else if self.count_start != 0 {
401                    to_extend = &self.escape.0[..self.count_start];
402                    self.count_start = 0;
403                }
404                if self.depth > 0 && byte == &self.escape.1[self.count_end] {
405                    self.count_end += 1;
406                    if self.depth == 1 {
407                        write_byte = false;
408                    }
409                } else if self.count_end != 0 {
410                    to_extend = &self.escape.1[..self.count_end];
411                    self.count_end = 0;
412                }
413                if self.count_start == self.escape.0.len() {
414                    self.depth += 1;
415                    self.count_start = 0;
416                    self.unescapable = true;
417                }
418                if self.count_end == self.escape.0.len() {
419                    self.depth -= 1;
420                    self.count_end = 0;
421                    if self.depth == 0 {
422                        self.buf.extend(
423                            eval(
424                                self.mapping,
425                                std::str::from_utf8(&self.var).map_err(to_io_error)?,
426                                self.escape,
427                                self.unescape,
428                            )
429                            .map_err(to_io_error)?
430                            .as_bytes(),
431                        );
432                        self.var.clear();
433                    }
434                }
435                if self.depth == 0 {
436                    self.buf.extend(to_extend);
437                    if write_byte {
438                        self.buf.push_back(*byte);
439                    }
440                } else {
441                    self.var.extend_from_slice(to_extend);
442                    if write_byte {
443                        self.var.push(*byte);
444                    }
445                }
446            }
447            written = std::cmp::min(buf.len(), self.buf.len());
448            for (i, elem) in self.buf.drain(0..written).enumerate() {
449                buf[i] = elem;
450            }
451            if in_bytes == 0 {
452                break;
453            }
454        }
455        Ok(written)
456    }
457}