Skip to content

Commit 0af5eb2

Browse files
authored
CFEngine extensions (#1)
1 parent 31ed9af commit 0af5eb2

File tree

5 files changed

+625
-26
lines changed

5 files changed

+625
-26
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ license = "MIT/Apache-2.0"
99

1010
[features]
1111
unstable = []
12+
CFEngine = []
1213

1314
[dependencies]
1415
log = "0.3.5"
1516
serde = "1.0.0"
17+
serde_json = "1.0.0"
1618

1719
[dev-dependencies]
1820
serde_derive = "1.0.0"

src/data.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
use std::cell::RefCell;
1+
use serde::ser::{SerializeMap, SerializeSeq};
22
use std::collections::HashMap;
33
use std::fmt;
4+
use std::{cell::RefCell, collections::BTreeMap};
45

56
// for bug!
67
use log::{error, log};
@@ -14,6 +15,35 @@ pub enum Data {
1415
Fun(RefCell<Box<dyn FnMut(String) -> String + Send>>),
1516
}
1617

18+
impl serde::Serialize for Data {
19+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20+
where
21+
S: serde::Serializer,
22+
{
23+
match *self {
24+
Data::Null => serializer.serialize_none(),
25+
Data::String(ref v) => serializer.serialize_str(v),
26+
Data::Bool(v) => serializer.serialize_bool(v),
27+
Data::Vec(ref v) => {
28+
let mut seq = serializer.serialize_seq(Some(v.len()))?;
29+
for e in v {
30+
seq.serialize_element(e)?;
31+
}
32+
seq.end()
33+
}
34+
Data::Map(ref v) => {
35+
let v: BTreeMap<_, _> = v.into_iter().collect();
36+
let mut map = serializer.serialize_map(Some(v.len()))?;
37+
for (k, va) in v {
38+
map.serialize_entry(k, va)?;
39+
}
40+
map.end()
41+
}
42+
Data::Fun(_) => serializer.serialize_unit(),
43+
}
44+
}
45+
}
46+
1747
impl PartialEq for Data {
1848
#[inline]
1949
fn eq(&self, other: &Data) -> bool {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
extern crate log;
55
extern crate serde;
6+
extern crate serde_json;
67

78
use std::path::{Path, PathBuf};
89
use std::result;

src/parser.rs

Lines changed: 102 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ pub enum Token {
2323
),
2424
IncompleteSection(Vec<String>, bool, String, bool),
2525
Partial(String, String, String),
26+
#[cfg(feature = "CFEngine")]
27+
At,
28+
#[cfg(feature = "CFEngine")]
29+
JSON(Vec<String>, String),
30+
#[cfg(feature = "CFEngine")]
31+
JSONMulti(Vec<String>, String),
32+
#[cfg(feature = "CFEngine")]
33+
TopJSON(Vec<String>, String),
34+
#[cfg(feature = "CFEngine")]
35+
TopJSONMulti(Vec<String>, String),
36+
#[cfg(feature = "CFEngine")]
37+
TopSection(Vec<Token>),
2638
}
2739

2840
/// Error type to represent parsing failure.
@@ -374,15 +386,38 @@ impl<'a, T: Iterator<Item = char>> Parser<'a, T> {
374386
// ignore comments
375387
self.eat_whitespace();
376388
}
377-
'&' => {
389+
#[cfg(feature = "CFEngine")]
390+
'%' => {
391+
// Data to be rendered as multi-line JSON representation
378392
let name = &content[1..len];
379393
let name = get_name_or_implicit(name)?;
394+
395+
self.tokens
396+
.push(if name.first() == Some(&"-top-".to_string()) {
397+
Token::TopJSONMulti(name, tag)
398+
} else {
399+
Token::JSONMulti(name, tag)
400+
});
401+
}
402+
#[cfg(feature = "CFEngine")]
403+
'$' => {
404+
// Data to be rendered as compact JSON representation
405+
let name = get_name_or_implicit(&content[1..len])?;
406+
407+
self.tokens
408+
.push(if name.first() == Some(&"-top-".to_string()) {
409+
Token::TopJSON(name, tag)
410+
} else {
411+
Token::JSON(name, tag)
412+
});
413+
}
414+
'&' => {
415+
let name = get_name_or_implicit(&content[1..len])?;
380416
self.tokens.push(Token::UnescapedTag(name, tag));
381417
}
382418
'{' => {
383419
if content.ends_with('}') {
384-
let name = &content[1..len - 1];
385-
let name = get_name_or_implicit(name)?;
420+
let name = get_name_or_implicit(&content[1..len - 1])?;
386421
self.tokens.push(Token::UnescapedTag(name, tag));
387422
} else {
388423
return Err(Error::UnbalancedUnescapeTag);
@@ -402,6 +437,10 @@ impl<'a, T: Iterator<Item = char>> Parser<'a, T> {
402437
self.tokens
403438
.push(Token::IncompleteSection(name, true, tag, newlined));
404439
}
440+
#[cfg(feature = "CFEngine")]
441+
'@' => {
442+
self.tokens.push(Token::At);
443+
}
405444
'/' => {
406445
self.eat_whitespace();
407446

@@ -423,6 +462,12 @@ impl<'a, T: Iterator<Item = char>> Parser<'a, T> {
423462
let mut srcs = Vec::new();
424463
for child in children.iter() {
425464
match *child {
465+
#[cfg(feature = "CFEngine")]
466+
Token::JSON(_, ref s)
467+
| Token::JSONMulti(_, ref s)
468+
| Token::TopJSON(_, ref s)
469+
| Token::TopJSONMulti(_, ref s) => srcs.push(s.clone()),
470+
426471
Token::Text(ref s)
427472
| Token::EscapedTag(_, ref s)
428473
| Token::UnescapedTag(_, ref s)
@@ -455,6 +500,24 @@ impl<'a, T: Iterator<Item = char>> Parser<'a, T> {
455500
src.push_str(s);
456501
}
457502

503+
#[cfg(feature = "CFEngine")]
504+
self.tokens
505+
.push(if name.first() == Some(&"-top-".to_string()) {
506+
Token::TopSection(children)
507+
} else {
508+
Token::Section(
509+
name,
510+
inverted,
511+
children,
512+
self.opening_tag.clone(),
513+
osection,
514+
src,
515+
tag,
516+
self.closing_tag.clone(),
517+
)
518+
});
519+
520+
#[cfg(not(feature = "CFEngine"))]
458521
self.tokens.push(Token::Section(
459522
name,
460523
inverted,
@@ -465,6 +528,7 @@ impl<'a, T: Iterator<Item = char>> Parser<'a, T> {
465528
tag,
466529
self.closing_tag.clone(),
467530
));
531+
468532
break;
469533
} else {
470534
return Err(Error::UnclosedSection(section_name.join(".")));
@@ -763,4 +827,39 @@ mod tests {
763827
let input = "{{=<% %>=}} <%{ %>";
764828
assert_eq!(parse(input), Err(Error::UnbalancedUnescapeTag))
765829
}
830+
831+
mod cfengine {
832+
use super::*;
833+
834+
#[test]
835+
fn test_json_multi() {
836+
assert!(parse("{{%var}}").is_ok());
837+
}
838+
839+
#[test]
840+
fn test_json_compact() {
841+
assert!(parse("{{$var}}").is_ok());
842+
}
843+
844+
#[test]
845+
fn test_top() {
846+
assert!(parse("{{%-top-}}").is_ok());
847+
assert!(parse("{{$-top-}}").is_ok());
848+
}
849+
850+
#[test]
851+
fn test_top_section() {
852+
assert!(parse("{{#-top-}} {{.}}{{/-top-}}").is_ok());
853+
}
854+
855+
#[test]
856+
fn test_at() {
857+
assert!(parse("{{#-top-}} {{{@}}}{{/-top-}}").is_ok());
858+
}
859+
860+
#[test]
861+
fn test_at_dot() {
862+
assert!(parse("{{#-top-}} {{{@}}} {{.}}{{/-top-}}").is_ok());
863+
}
864+
}
766865
}

0 commit comments

Comments
 (0)