gtmpl/
node.rs

1use std::fmt::{Display, Formatter};
2
3use crate::error::NodeError;
4use crate::lexer::ItemType;
5use crate::utils::unquote_char;
6
7use gtmpl_value::Value;
8
9macro_rules! nodes {
10    ($($node:ident, $name:ident),*) => {
11        #[derive(Debug)]
12        #[derive(Clone)]
13        #[derive(PartialEq)]
14        pub enum NodeType {
15           $($name,)*
16        }
17
18        #[derive(Clone)]
19        #[derive(Debug)]
20        pub enum Nodes {
21            $($name($node),)*
22        }
23
24        impl Display for Nodes {
25            fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
26                match *self {
27                    $(Nodes::$name(ref t) => t.fmt(f),)*
28                }
29            }
30        }
31
32        impl Nodes {
33            pub fn typ(&self) -> &NodeType {
34                match *self {
35                    $(Nodes::$name(ref t) => t.typ(),)*
36                }
37            }
38            pub fn pos(&self) -> Pos {
39                match *self {
40                    $(Nodes::$name(ref t) => t.pos(),)*
41                }
42            }
43            pub fn tree(&self) -> TreeId {
44                match *self {
45                    $(Nodes::$name(ref t) => t.tree(),)*
46                }
47            }
48        }
49    }
50}
51
52nodes!(
53    ListNode,
54    List,
55    TextNode,
56    Text,
57    PipeNode,
58    Pipe,
59    ActionNode,
60    Action,
61    CommandNode,
62    Command,
63    IdentifierNode,
64    Identifier,
65    VariableNode,
66    Variable,
67    DotNode,
68    Dot,
69    NilNode,
70    Nil,
71    FieldNode,
72    Field,
73    ChainNode,
74    Chain,
75    BoolNode,
76    Bool,
77    NumberNode,
78    Number,
79    StringNode,
80    String,
81    EndNode,
82    End,
83    ElseNode,
84    Else,
85    IfNode,
86    If,
87    WithNode,
88    With,
89    RangeNode,
90    Range,
91    TemplateNode,
92    Template
93);
94
95pub type Pos = usize;
96
97pub type TreeId = usize;
98
99pub trait Node: Display {
100    fn typ(&self) -> &NodeType;
101    fn pos(&self) -> Pos;
102    fn tree(&self) -> TreeId;
103}
104
105macro_rules! node {
106    ($name:ident {
107        $($field:ident : $typ:ty),* $(,)*
108    }) => {
109        #[derive(Clone)]
110        #[derive(Debug)]
111        pub struct $name {
112            typ: NodeType,
113            pos: Pos,
114            tr: TreeId,
115            $(pub $field: $typ,)*
116        }
117        impl Node for $name {
118            fn typ(&self) -> &NodeType {
119                &self.typ
120            }
121            fn pos(&self) -> Pos {
122                self.pos
123            }
124            fn tree(&self) -> TreeId {
125                self.tr
126            }
127        }
128    }
129}
130
131impl Nodes {
132    pub fn is_empty_tree(&self) -> Result<bool, NodeError> {
133        match *self {
134            Nodes::List(ref n) => n.is_empty_tree(),
135            Nodes::Text(ref n) => Ok(n.text.is_empty()),
136            Nodes::Action(_)
137            | Nodes::If(_)
138            | Nodes::Range(_)
139            | Nodes::Template(_)
140            | Nodes::With(_) => Ok(false),
141            _ => Err(NodeError::NaTN),
142        }
143    }
144}
145
146node!(
147    ListNode {
148        nodes: Vec<Nodes>
149    }
150);
151
152impl ListNode {
153    pub fn append(&mut self, n: Nodes) {
154        self.nodes.push(n);
155    }
156    pub fn new(tr: TreeId, pos: Pos) -> ListNode {
157        ListNode {
158            typ: NodeType::List,
159            pos,
160            tr,
161            nodes: vec![],
162        }
163    }
164    pub fn is_empty_tree(&self) -> Result<bool, NodeError> {
165        for n in &self.nodes {
166            match n.is_empty_tree() {
167                Ok(true) => {}
168                Ok(false) => return Ok(false),
169                Err(s) => return Err(s),
170            }
171        }
172        Ok(true)
173    }
174}
175
176impl Display for ListNode {
177    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
178        for n in &self.nodes {
179            if let Err(e) = n.fmt(f) {
180                return Err(e);
181            }
182        }
183        Ok(())
184    }
185}
186
187node!(TextNode { text: String });
188
189impl TextNode {
190    pub fn new(tr: TreeId, pos: Pos, text: String) -> TextNode {
191        TextNode {
192            typ: NodeType::Text,
193            pos,
194            tr,
195            text,
196        }
197    }
198}
199
200impl Display for TextNode {
201    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
202        write!(f, "{}", self.text)
203    }
204}
205
206node!(
207    PipeNode {
208        decl: Vec<VariableNode>,
209        cmds: Vec<CommandNode>
210    }
211);
212
213impl PipeNode {
214    pub fn new(tr: TreeId, pos: Pos, decl: Vec<VariableNode>) -> PipeNode {
215        PipeNode {
216            typ: NodeType::Pipe,
217            tr,
218            pos,
219            decl,
220            cmds: vec![],
221        }
222    }
223
224    pub fn append(&mut self, cmd: CommandNode) {
225        self.cmds.push(cmd);
226    }
227}
228
229impl Display for PipeNode {
230    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
231        let decl = if self.decl.is_empty() {
232            Ok(())
233        } else {
234            write!(
235                f,
236                "{} := ",
237                self.decl
238                    .iter()
239                    .map(|n| n.to_string())
240                    .collect::<Vec<String>>()
241                    .join(", ")
242            )
243        };
244        decl.and_then(|_| {
245            write!(
246                f,
247                "{}",
248                self.cmds
249                    .iter()
250                    .map(|cmd| cmd.to_string())
251                    .collect::<Vec<String>>()
252                    .join(" | ")
253            )
254        })
255    }
256}
257
258node!(ActionNode { pipe: PipeNode });
259
260impl ActionNode {
261    pub fn new(tr: TreeId, pos: Pos, pipe: PipeNode) -> ActionNode {
262        ActionNode {
263            typ: NodeType::Action,
264            tr,
265            pos,
266            pipe,
267        }
268    }
269}
270
271impl Display for ActionNode {
272    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
273        write!(f, "{{{{{}}}}}", self.pipe)
274    }
275}
276
277node!(
278    CommandNode {
279        args: Vec<Nodes>
280    }
281);
282
283impl CommandNode {
284    pub fn new(tr: TreeId, pos: Pos) -> CommandNode {
285        CommandNode {
286            typ: NodeType::Command,
287            pos,
288            tr,
289            args: vec![],
290        }
291    }
292
293    pub fn append(&mut self, node: Nodes) {
294        self.args.push(node);
295    }
296}
297
298impl Display for CommandNode {
299    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
300        let s = self
301            .args
302            .iter()
303            .map(|n|
304                // Handle PipeNode.
305                n.to_string())
306            .collect::<Vec<String>>()
307            .join(" ");
308        write!(f, "{}", s)
309    }
310}
311
312node!(IdentifierNode { ident: String });
313
314impl IdentifierNode {
315    pub fn new(ident: String) -> IdentifierNode {
316        IdentifierNode {
317            typ: NodeType::Identifier,
318            tr: 0,
319            pos: 0,
320            ident,
321        }
322    }
323
324    pub fn set_pos(&mut self, pos: Pos) -> &IdentifierNode {
325        self.pos = pos;
326        self
327    }
328
329    pub fn set_tree(&mut self, tr: TreeId) -> &IdentifierNode {
330        self.tr = tr;
331        self
332    }
333}
334
335impl Display for IdentifierNode {
336    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
337        write!(f, "{}", self.ident)
338    }
339}
340
341node!(
342    VariableNode {
343        ident: Vec<String>
344    }
345);
346
347impl VariableNode {
348    pub fn new(tr: TreeId, pos: Pos, ident: &str) -> VariableNode {
349        VariableNode {
350            typ: NodeType::Variable,
351            tr,
352            pos,
353            ident: ident.split('.').map(|s| s.to_owned()).collect(),
354        }
355    }
356}
357
358impl Display for VariableNode {
359    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
360        write!(f, "{}", self.ident.join("."))
361    }
362}
363
364node!(DotNode {});
365
366impl DotNode {
367    pub fn new(tr: TreeId, pos: Pos) -> DotNode {
368        DotNode {
369            typ: NodeType::Dot,
370            tr,
371            pos,
372        }
373    }
374}
375
376impl Display for DotNode {
377    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
378        write!(f, ".")
379    }
380}
381
382node!(NilNode {});
383
384impl Display for NilNode {
385    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
386        write!(f, "nil")
387    }
388}
389
390impl NilNode {
391    pub fn new(tr: TreeId, pos: Pos) -> NilNode {
392        NilNode {
393            typ: NodeType::Nil,
394            tr,
395            pos,
396        }
397    }
398}
399
400node!(
401    FieldNode {
402        ident: Vec<String>
403    }
404);
405
406impl FieldNode {
407    pub fn new(tr: TreeId, pos: Pos, ident: &str) -> FieldNode {
408        FieldNode {
409            typ: NodeType::Field,
410            tr,
411            pos,
412            ident: ident[..]
413                .split('.')
414                .filter_map(|s| {
415                    if s.is_empty() {
416                        None
417                    } else {
418                        Some(s.to_owned())
419                    }
420                })
421                .collect(),
422        }
423    }
424}
425
426impl Display for FieldNode {
427    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
428        write!(f, "{}", self.ident.join("."))
429    }
430}
431
432node!(
433    ChainNode {
434        node: Box<Nodes>,
435        field: Vec<String>
436    }
437);
438
439impl ChainNode {
440    pub fn new(tr: TreeId, pos: Pos, node: Nodes) -> ChainNode {
441        ChainNode {
442            typ: NodeType::Chain,
443            tr,
444            pos,
445            node: Box::new(node),
446            field: vec![],
447        }
448    }
449
450    pub fn add(&mut self, val: &str) {
451        let val = val.trim_start_matches('.').to_owned();
452        self.field.push(val);
453    }
454}
455
456impl Display for ChainNode {
457    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
458        if let Err(e) = {
459            // Handle PipeNode.
460            write!(f, "{}", self.node)
461        } {
462            return Err(e);
463        }
464        for field in &self.field {
465            if let Err(e) = write!(f, ".{}", field) {
466                return Err(e);
467            }
468        }
469        Ok(())
470    }
471}
472
473node!(BoolNode { value: Value });
474
475impl BoolNode {
476    pub fn new(tr: TreeId, pos: Pos, val: bool) -> BoolNode {
477        BoolNode {
478            typ: NodeType::Bool,
479            tr,
480            pos,
481            value: Value::from(val),
482        }
483    }
484}
485
486impl Display for BoolNode {
487    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
488        write!(f, "{}", self.value)
489    }
490}
491
492#[derive(Clone, Debug)]
493pub enum NumberType {
494    U64,
495    I64,
496    Float,
497    Char,
498}
499
500node!(NumberNode {
501    is_i64: bool,
502    is_u64: bool,
503    is_f64: bool,
504    text: String,
505    number_typ: NumberType,
506    value: Value,
507});
508
509impl NumberNode {
510    #[cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
511    pub fn new(
512        tr: TreeId,
513        pos: Pos,
514        text: String,
515        item_typ: &ItemType,
516    ) -> Result<NumberNode, NodeError> {
517        match *item_typ {
518            ItemType::ItemCharConstant => unquote_char(&text, '\'')
519                .map(|c| NumberNode {
520                    typ: NodeType::Number,
521                    tr,
522                    pos,
523                    is_i64: true,
524                    is_u64: true,
525                    is_f64: true,
526                    text,
527                    number_typ: NumberType::Char,
528                    value: Value::from(c as u64),
529                })
530                .ok_or(NodeError::UnquoteError),
531            _ => {
532                let mut number_typ = NumberType::Float;
533
534                // TODO: Deal with hex.
535                let (mut as_i64, mut is_i64) = text
536                    .parse::<i64>()
537                    .map(|i| (i, true))
538                    .unwrap_or((0i64, false));
539
540                if is_i64 {
541                    number_typ = NumberType::I64;
542                }
543
544                let (mut as_u64, mut is_u64) = text
545                    .parse::<u64>()
546                    .map(|i| (i, true))
547                    .unwrap_or((0u64, false));
548
549                if is_u64 {
550                    number_typ = NumberType::U64;
551                }
552
553                if is_i64 && as_i64 == 0 {
554                    // In case of -0.
555                    as_u64 = 0;
556                    is_u64 = true;
557                }
558
559                let (as_f64, is_f64) = match text.parse::<f64>() {
560                    Err(_) => (0.0_f64, false),
561                    Ok(f) => {
562                        let frac = text.contains(|c| {
563                            matches! {
564                            c, '.' | 'e' | 'E' }
565                        });
566                        if frac {
567                            (f, true)
568                        } else {
569                            (f, false)
570                        }
571                    }
572                };
573                if !is_i64 && ((as_f64 as i64) as f64) == as_f64 {
574                    as_i64 = as_f64 as i64;
575                    is_i64 = true;
576                }
577                if !is_u64 && ((as_f64 as u64) as f64) == as_f64 {
578                    as_u64 = as_f64 as u64;
579                    is_u64 = true;
580                }
581                if !is_u64 && !is_i64 && !is_f64 {
582                    return Err(NodeError::NaN);
583                }
584
585                let value = if is_u64 {
586                    Value::from(as_u64)
587                } else if is_i64 {
588                    Value::from(as_i64)
589                } else {
590                    Value::from(as_f64)
591                };
592
593                Ok(NumberNode {
594                    typ: NodeType::Number,
595                    tr,
596                    pos,
597                    is_i64,
598                    is_u64,
599                    is_f64,
600                    text,
601                    number_typ,
602                    value,
603                })
604            }
605        }
606    }
607}
608
609impl Display for NumberNode {
610    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
611        write!(f, "{}", self.text)
612    }
613}
614
615node!(StringNode {
616    quoted: String,
617    value: Value,
618});
619
620impl StringNode {
621    pub fn new(tr: TreeId, pos: Pos, orig: String, text: String) -> StringNode {
622        StringNode {
623            typ: NodeType::String,
624            tr,
625            pos,
626            quoted: orig,
627            value: Value::from(text),
628        }
629    }
630}
631
632impl Display for StringNode {
633    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
634        write!(f, "{}", self.quoted)
635    }
636}
637
638node!(EndNode {});
639
640impl EndNode {
641    pub fn new(tr: TreeId, pos: Pos) -> EndNode {
642        EndNode {
643            typ: NodeType::End,
644            tr,
645            pos,
646        }
647    }
648}
649
650impl Display for EndNode {
651    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
652        write!(f, "{{{{end}}}}")
653    }
654}
655
656node!(ElseNode {});
657
658impl ElseNode {
659    pub fn new(tr: TreeId, pos: Pos) -> ElseNode {
660        ElseNode {
661            typ: NodeType::Else,
662            tr,
663            pos,
664        }
665    }
666}
667
668impl Display for ElseNode {
669    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
670        write!(f, "{{{{else}}}}")
671    }
672}
673
674node!(
675    BranchNode {
676        pipe: PipeNode,
677        list: ListNode,
678        else_list: Option<ListNode>
679    }
680);
681
682pub type IfNode = BranchNode;
683pub type WithNode = BranchNode;
684pub type RangeNode = BranchNode;
685
686impl BranchNode {
687    pub fn new_if(
688        tr: TreeId,
689        pos: Pos,
690        pipe: PipeNode,
691        list: ListNode,
692        else_list: Option<ListNode>,
693    ) -> IfNode {
694        IfNode {
695            typ: NodeType::If,
696            tr,
697            pos,
698            pipe,
699            list,
700            else_list,
701        }
702    }
703
704    pub fn new_with(
705        tr: TreeId,
706        pos: Pos,
707        pipe: PipeNode,
708        list: ListNode,
709        else_list: Option<ListNode>,
710    ) -> WithNode {
711        WithNode {
712            typ: NodeType::With,
713            tr,
714            pos,
715            pipe,
716            list,
717            else_list,
718        }
719    }
720
721    pub fn new_range(
722        tr: TreeId,
723        pos: Pos,
724        pipe: PipeNode,
725        list: ListNode,
726        else_list: Option<ListNode>,
727    ) -> RangeNode {
728        RangeNode {
729            typ: NodeType::Range,
730            tr,
731            pos,
732            pipe,
733            list,
734            else_list,
735        }
736    }
737}
738
739impl Display for BranchNode {
740    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
741        let name = match self.typ {
742            NodeType::If => "if",
743            NodeType::Range => "range",
744            NodeType::With => "with",
745            _ => {
746                return Err(std::fmt::Error);
747            }
748        };
749        if let Some(ref else_list) = self.else_list {
750            return write!(
751                f,
752                "{{{{{} {}}}}}{}{{{{else}}}}{}{{{{end}}}}",
753                name, self.pipe, self.list, else_list
754            );
755        }
756        write!(f, "{{{{{} {}}}}}{}{{{{end}}}}", name, self.pipe, self.list)
757    }
758}
759
760node!(
761    TemplateNode {
762        name: PipeOrString,
763        pipe: Option<PipeNode>
764    }
765);
766
767impl TemplateNode {
768    pub fn new(tr: TreeId, pos: Pos, name: PipeOrString, pipe: Option<PipeNode>) -> TemplateNode {
769        TemplateNode {
770            typ: NodeType::Template,
771            tr,
772            pos,
773            name,
774            pipe,
775        }
776    }
777}
778
779impl Display for TemplateNode {
780    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
781        match self.pipe {
782            Some(ref pipe) => write!(f, "{{{{template {} {}}}}}", self.name, pipe),
783            None => write!(f, "{{{{template {}}}}}", self.name),
784        }
785    }
786}
787
788#[derive(Clone, Debug)]
789pub enum PipeOrString {
790    Pipe(PipeNode),
791    String(String),
792}
793
794impl Display for PipeOrString {
795    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
796        match *self {
797            PipeOrString::Pipe(ref pipe_node) => write!(f, "{}", pipe_node),
798            PipeOrString::String(ref s) => write!(f, "{}", s),
799        }
800    }
801}
802
803#[cfg(test)]
804mod tests {
805    use super::*;
806
807    #[test]
808    fn test_clone() {
809        let t1 = TextNode::new(1, 0, "foo".to_owned());
810        let mut t2 = t1.clone();
811        t2.text = "bar".to_owned();
812        assert_eq!(t1.to_string(), "foo");
813        assert_eq!(t2.to_string(), "bar");
814    }
815
816    #[test]
817    fn test_end() {
818        let t1 = EndNode::new(1, 0);
819        assert_eq!(t1.to_string(), "{{end}}");
820    }
821}