go_template/
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        is_assign : bool,
211    }
212);
213
214impl PipeNode {
215    pub fn new(tr: TreeId, pos: Pos, decl: Vec<VariableNode>, is_assign: bool) -> PipeNode {
216        PipeNode {
217            typ: NodeType::Pipe,
218            tr,
219            pos,
220            decl,
221            is_assign: is_assign,
222            cmds: vec![],
223        }
224    }
225
226    pub fn append(&mut self, cmd: CommandNode) {
227        self.cmds.push(cmd);
228    }
229}
230
231impl Display for PipeNode {
232    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
233        let decl = if self.decl.is_empty() {
234            Ok(())
235        } else {
236            write!(
237                f,
238                "{} := ",
239                self.decl
240                    .iter()
241                    .map(|n| n.to_string())
242                    .collect::<Vec<String>>()
243                    .join(", ")
244            )
245        };
246        decl.and_then(|_| {
247            write!(
248                f,
249                "{}",
250                self.cmds
251                    .iter()
252                    .map(|cmd| cmd.to_string())
253                    .collect::<Vec<String>>()
254                    .join(" | ")
255            )
256        })
257    }
258}
259
260node!(ActionNode { pipe: PipeNode });
261
262impl ActionNode {
263    pub fn new(tr: TreeId, pos: Pos, pipe: PipeNode) -> ActionNode {
264        ActionNode {
265            typ: NodeType::Action,
266            tr,
267            pos,
268            pipe,
269        }
270    }
271}
272
273impl Display for ActionNode {
274    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
275        write!(f, "{{{{{}}}}}", self.pipe)
276    }
277}
278
279node!(
280    CommandNode {
281        args: Vec<Nodes>
282    }
283);
284
285impl CommandNode {
286    pub fn new(tr: TreeId, pos: Pos) -> CommandNode {
287        CommandNode {
288            typ: NodeType::Command,
289            pos,
290            tr,
291            args: vec![],
292        }
293    }
294
295    pub fn append(&mut self, node: Nodes) {
296        self.args.push(node);
297    }
298}
299
300impl Display for CommandNode {
301    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
302        let s = self
303            .args
304            .iter()
305            .map(|n|
306                // Handle PipeNode.
307                n.to_string())
308            .collect::<Vec<String>>()
309            .join(" ");
310        write!(f, "{}", s)
311    }
312}
313
314node!(IdentifierNode { ident: String });
315
316impl IdentifierNode {
317    pub fn new(ident: String) -> IdentifierNode {
318        IdentifierNode {
319            typ: NodeType::Identifier,
320            tr: 0,
321            pos: 0,
322            ident,
323        }
324    }
325
326    pub fn set_pos(&mut self, pos: Pos) -> &IdentifierNode {
327        self.pos = pos;
328        self
329    }
330
331    pub fn set_tree(&mut self, tr: TreeId) -> &IdentifierNode {
332        self.tr = tr;
333        self
334    }
335}
336
337impl Display for IdentifierNode {
338    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
339        write!(f, "{}", self.ident)
340    }
341}
342
343node!(
344    VariableNode {
345        ident: Vec<String>
346    }
347);
348
349impl VariableNode {
350    pub fn new(tr: TreeId, pos: Pos, ident: &str) -> VariableNode {
351        VariableNode {
352            typ: NodeType::Variable,
353            tr,
354            pos,
355            ident: ident.split('.').map(|s| s.to_owned()).collect(),
356        }
357    }
358}
359
360impl Display for VariableNode {
361    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
362        write!(f, "{}", self.ident.join("."))
363    }
364}
365
366node!(DotNode {});
367
368impl DotNode {
369    pub fn new(tr: TreeId, pos: Pos) -> DotNode {
370        DotNode {
371            typ: NodeType::Dot,
372            tr,
373            pos,
374        }
375    }
376}
377
378impl Display for DotNode {
379    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
380        write!(f, ".")
381    }
382}
383
384node!(NilNode {});
385
386impl Display for NilNode {
387    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
388        write!(f, "nil")
389    }
390}
391
392impl NilNode {
393    pub fn new(tr: TreeId, pos: Pos) -> NilNode {
394        NilNode {
395            typ: NodeType::Nil,
396            tr,
397            pos,
398        }
399    }
400}
401
402node!(
403    FieldNode {
404        ident: Vec<String>
405    }
406);
407
408impl FieldNode {
409    pub fn new(tr: TreeId, pos: Pos, ident: &str) -> FieldNode {
410        FieldNode {
411            typ: NodeType::Field,
412            tr,
413            pos,
414            ident: ident[..]
415                .split('.')
416                .filter_map(|s| {
417                    if s.is_empty() {
418                        None
419                    } else {
420                        Some(s.to_owned())
421                    }
422                })
423                .collect(),
424        }
425    }
426}
427
428impl Display for FieldNode {
429    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
430        write!(f, "{}", self.ident.join("."))
431    }
432}
433
434node!(
435    ChainNode {
436        node: Box<Nodes>,
437        field: Vec<String>
438    }
439);
440
441impl ChainNode {
442    pub fn new(tr: TreeId, pos: Pos, node: Nodes) -> ChainNode {
443        ChainNode {
444            typ: NodeType::Chain,
445            tr,
446            pos,
447            node: Box::new(node),
448            field: vec![],
449        }
450    }
451
452    pub fn add(&mut self, val: &str) {
453        let val = val.trim_start_matches('.').to_owned();
454        self.field.push(val);
455    }
456}
457
458impl Display for ChainNode {
459    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
460        if let Err(e) = {
461            // Handle PipeNode.
462            write!(f, "{}", self.node)
463        } {
464            return Err(e);
465        }
466        for field in &self.field {
467            if let Err(e) = write!(f, ".{}", field) {
468                return Err(e);
469            }
470        }
471        Ok(())
472    }
473}
474
475node!(BoolNode { value: Value });
476
477impl BoolNode {
478    pub fn new(tr: TreeId, pos: Pos, val: bool) -> BoolNode {
479        BoolNode {
480            typ: NodeType::Bool,
481            tr,
482            pos,
483            value: Value::from(val),
484        }
485    }
486}
487
488impl Display for BoolNode {
489    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
490        write!(f, "{}", self.value)
491    }
492}
493
494#[derive(Clone, Debug)]
495pub enum NumberType {
496    U64,
497    I64,
498    Float,
499    Char,
500}
501
502node!(NumberNode {
503    is_i64: bool,
504    is_u64: bool,
505    is_f64: bool,
506    text: String,
507    number_typ: NumberType,
508    value: Value,
509});
510
511impl NumberNode {
512    #[cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
513    pub fn new(
514        tr: TreeId,
515        pos: Pos,
516        text: String,
517        item_typ: &ItemType,
518    ) -> Result<NumberNode, NodeError> {
519        match *item_typ {
520            ItemType::ItemCharConstant => unquote_char(&text, '\'')
521                .map(|c| NumberNode {
522                    typ: NodeType::Number,
523                    tr,
524                    pos,
525                    is_i64: true,
526                    is_u64: true,
527                    is_f64: true,
528                    text,
529                    number_typ: NumberType::Char,
530                    value: Value::from(c as u64),
531                })
532                .ok_or(NodeError::UnquoteError),
533            _ => {
534                let mut number_typ = NumberType::Float;
535
536                // TODO: Deal with hex.
537                let (mut as_i64, mut is_i64) = text
538                    .parse::<i64>()
539                    .map(|i| (i, true))
540                    .unwrap_or((0i64, false));
541
542                if is_i64 {
543                    number_typ = NumberType::I64;
544                }
545
546                let (mut as_u64, mut is_u64) = text
547                    .parse::<u64>()
548                    .map(|i| (i, true))
549                    .unwrap_or((0u64, false));
550
551                if is_u64 {
552                    number_typ = NumberType::U64;
553                }
554
555                if is_i64 && as_i64 == 0 {
556                    // In case of -0.
557                    as_u64 = 0;
558                    is_u64 = true;
559                }
560
561                let (as_f64, is_f64) = match text.parse::<f64>() {
562                    Err(_) => (0.0_f64, false),
563                    Ok(f) => {
564                        let frac = text.contains(|c| match c {
565                            '.' | 'e' | 'E' => true,
566                            _ => false,
567                        });
568                        if frac {
569                            (f, true)
570                        } else {
571                            (f, false)
572                        }
573                    }
574                };
575                if !is_i64 && ((as_f64 as i64) as f64) == as_f64 {
576                    as_i64 = as_f64 as i64;
577                    is_i64 = true;
578                }
579                if !is_u64 && ((as_f64 as u64) as f64) == as_f64 {
580                    as_u64 = as_f64 as u64;
581                    is_u64 = true;
582                }
583                if !is_u64 && !is_i64 && !is_f64 {
584                    return Err(NodeError::NaN);
585                }
586
587                let value = if is_u64 {
588                    Value::from(as_u64)
589                } else if is_i64 {
590                    Value::from(as_i64)
591                } else {
592                    Value::from(as_f64)
593                };
594
595                Ok(NumberNode {
596                    typ: NodeType::Number,
597                    tr,
598                    pos,
599                    is_i64,
600                    is_u64,
601                    is_f64,
602                    text,
603                    number_typ,
604                    value,
605                })
606            }
607        }
608    }
609}
610
611impl Display for NumberNode {
612    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
613        write!(f, "{}", self.text)
614    }
615}
616
617node!(StringNode {
618    quoted: String,
619    value: Value,
620});
621
622impl StringNode {
623    pub fn new(tr: TreeId, pos: Pos, orig: String, text: String) -> StringNode {
624        StringNode {
625            typ: NodeType::String,
626            tr,
627            pos,
628            quoted: orig,
629            value: Value::from(text),
630        }
631    }
632}
633
634impl Display for StringNode {
635    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
636        write!(f, "{}", self.quoted)
637    }
638}
639
640node!(EndNode {});
641
642impl EndNode {
643    pub fn new(tr: TreeId, pos: Pos) -> EndNode {
644        EndNode {
645            typ: NodeType::End,
646            tr,
647            pos,
648        }
649    }
650}
651
652impl Display for EndNode {
653    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
654        write!(f, "{{{{end}}}}")
655    }
656}
657
658node!(ElseNode {});
659
660impl ElseNode {
661    pub fn new(tr: TreeId, pos: Pos) -> ElseNode {
662        ElseNode {
663            typ: NodeType::Else,
664            tr,
665            pos,
666        }
667    }
668}
669
670impl Display for ElseNode {
671    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
672        write!(f, "{{{{else}}}}")
673    }
674}
675
676node!(
677    BranchNode {
678        pipe: PipeNode,
679        list: ListNode,
680        else_list: Option<ListNode>
681    }
682);
683
684pub type IfNode = BranchNode;
685pub type WithNode = BranchNode;
686pub type RangeNode = BranchNode;
687
688impl BranchNode {
689    pub fn new_if(
690        tr: TreeId,
691        pos: Pos,
692        pipe: PipeNode,
693        list: ListNode,
694        else_list: Option<ListNode>,
695    ) -> IfNode {
696        IfNode {
697            typ: NodeType::If,
698            tr,
699            pos,
700            pipe,
701            list,
702            else_list,
703        }
704    }
705
706    pub fn new_with(
707        tr: TreeId,
708        pos: Pos,
709        pipe: PipeNode,
710        list: ListNode,
711        else_list: Option<ListNode>,
712    ) -> WithNode {
713        WithNode {
714            typ: NodeType::With,
715            tr,
716            pos,
717            pipe,
718            list,
719            else_list,
720        }
721    }
722
723    pub fn new_range(
724        tr: TreeId,
725        pos: Pos,
726        pipe: PipeNode,
727        list: ListNode,
728        else_list: Option<ListNode>,
729    ) -> RangeNode {
730        RangeNode {
731            typ: NodeType::Range,
732            tr,
733            pos,
734            pipe,
735            list,
736            else_list,
737        }
738    }
739}
740
741impl Display for BranchNode {
742    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
743        let name = match self.typ {
744            NodeType::If => "if",
745            NodeType::Range => "range",
746            NodeType::With => "with",
747            _ => {
748                return Err(std::fmt::Error);
749            }
750        };
751        if let Some(ref else_list) = self.else_list {
752            return write!(
753                f,
754                "{{{{{} {}}}}}{}{{{{else}}}}{}{{{{end}}}}",
755                name, self.pipe, self.list, else_list
756            );
757        }
758        write!(f, "{{{{{} {}}}}}{}{{{{end}}}}", name, self.pipe, self.list)
759    }
760}
761
762node!(
763    TemplateNode {
764        name: PipeOrString,
765        pipe: Option<PipeNode>
766    }
767);
768
769impl TemplateNode {
770    pub fn new(tr: TreeId, pos: Pos, name: PipeOrString, pipe: Option<PipeNode>) -> TemplateNode {
771        TemplateNode {
772            typ: NodeType::Template,
773            tr,
774            pos,
775            name,
776            pipe,
777        }
778    }
779}
780
781impl Display for TemplateNode {
782    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
783        match self.pipe {
784            Some(ref pipe) => write!(f, "{{{{template {} {}}}}}", self.name, pipe),
785            None => write!(f, "{{{{template {}}}}}", self.name),
786        }
787    }
788}
789
790#[derive(Clone, Debug)]
791pub enum PipeOrString {
792    Pipe(PipeNode),
793    String(String),
794}
795
796impl Display for PipeOrString {
797    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
798        match *self {
799            PipeOrString::Pipe(ref pipe_node) => write!(f, "{}", pipe_node),
800            PipeOrString::String(ref s) => write!(f, "{}", s),
801        }
802    }
803}
804
805#[cfg(test)]
806mod tests {
807    use super::*;
808
809    #[test]
810    fn test_clone() {
811        let t1 = TextNode::new(1, 0, "foo".to_owned());
812        let mut t2 = t1.clone();
813        t2.text = "bar".to_owned();
814        assert_eq!(t1.to_string(), "foo");
815        assert_eq!(t2.to_string(), "bar");
816    }
817
818    #[test]
819    fn test_end() {
820        let t1 = EndNode::new(1, 0);
821        assert_eq!(t1.to_string(), "{{end}}");
822    }
823}