1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use super::*;

use std::collections::HashMap;


pub trait ResolveStrategy {
    fn resolve_interpolation(&mut self, interpolation: &Interpolation, node: &NodeRef, parent: &NodeRef, root: &NodeRef) -> Option<NodeRef>;
}

#[derive(Debug)]
pub struct DefaultResolveStrategy;

impl ResolveStrategy for DefaultResolveStrategy {
    fn resolve_interpolation(&mut self, interpolation: &Interpolation, _node: &NodeRef, parent: &NodeRef, root: &NodeRef) -> Option<NodeRef> {
        interpolation.resolve(root, parent)
    }
}


#[derive(Debug)]
pub struct RootedResolveStrategy;

impl ResolveStrategy for RootedResolveStrategy {
    fn resolve_interpolation(&mut self, interpolation: &Interpolation, _node: &NodeRef, _parent: &NodeRef, root: &NodeRef) -> Option<NodeRef> {
        interpolation.resolve(root, root)
    }
}


#[derive(Debug)]
pub struct TreeResolver {
    parser: self::interpolation::Parser,
}

impl TreeResolver {
    #[inline]
    pub fn new() -> TreeResolver {
        Self::with_parser(self::interpolation::Parser::new())
    }

    #[inline]
    pub fn with_delims(open_delim: &str, close_delim: &str) -> TreeResolver {
        Self::with_parser(self::interpolation::Parser::with_delims(open_delim, close_delim))
    }

    #[inline]
    pub fn with_parser(parser: self::interpolation::Parser) -> TreeResolver {
        TreeResolver {
            parser,
        }
    }

    pub fn resolve(&mut self, root: &NodeRef) {
        self.resolve_custom(DefaultResolveStrategy, root)
    }

    pub fn resolve_custom<P>(&mut self, mut strategy: P, root: &NodeRef) where P: ResolveStrategy {
        let mut replacements = Vec::new();
        let mut iter = 0;

        root.visit_recursive(|_r, p, n| {
            if n.is_string() {
                let i = self.parser.parse_str(&n.data().as_string()).unwrap_or(Interpolation::Empty);
                if !i.is_empty() {
                    let index = n.data().metadata().index();
                    let key = Symbol::from(n.data().metadata().key());
                    replacements.push((i, p.clone(), index, key));
                }
            }
            true
        });

        if replacements.is_empty() {
            return;
        }

        loop {
            iter += 1;
            if iter == 100 {
                panic!("too many iterations while resolving interpolations"); //FIXME (jc) add proper error handling
            }

            let mut change = false;
            for (i, p, index, key) in replacements.iter() {
                if let Some(nn) = strategy.resolve_interpolation(&i, &p, &p, root) {
                    let n = p.get_child_index(*index).unwrap();
                    if !n.is_identical_deep(&nn) {
                        change = true;
                        p.set_child(Some(*index), Some(key.clone()), nn.into_consumable());
                    }
                }
            }

            if !change {
                break;
            }
        }
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn can_interpolate_recursively() {
        let n = NodeRef::from_json(r#"
            {
                "child1": {
                    "my_key": "<% @key %>",
                    "sub2": "<% @.subchild %>",
                    "subchild": {
                        "my_key": "<% @^.my_key %>"
                    }
                }
            }
        "#).unwrap();

        let mut r = TreeResolver::new();
        r.resolve(&n);

        assert_eq!(n.to_json(), r#"{"child1":{"my_key":"child1","sub2":{"my_key":"child1"},"subchild":{"my_key":"child1"}}}"#);
    }
}