hcl/structure/
json_spec.rs1use super::{Block, Body, Structure};
2use crate::{Expression, Identifier, Map, Value};
3use indexmap::map::Entry;
4
5pub(crate) trait IntoJsonSpec: Sized {
11 fn into_json_spec(self) -> Expression {
16 Expression::from_iter(self.into_json_nodes())
17 }
18
19 fn into_json_nodes(self) -> Map<String, JsonNode>;
25}
26
27impl IntoJsonSpec for Body {
28 fn into_json_nodes(self) -> Map<String, JsonNode> {
29 self.into_iter().fold(Map::new(), |mut map, structure| {
30 match structure {
31 Structure::Attribute(attr) => {
32 map.insert(attr.key.into_inner(), JsonNode::Expr(attr.expr));
33 }
34 Structure::Block(block) => {
35 for (key, node) in block.into_json_nodes() {
36 node.deep_merge_into(&mut map, key);
37 }
38 }
39 }
40
41 map
42 })
43 }
44}
45
46impl IntoJsonSpec for Block {
47 fn into_json_nodes(self) -> Map<String, JsonNode> {
48 let mut labels = self.labels.into_iter();
49
50 let node = match labels.next() {
51 Some(label) => {
52 let block = Block {
53 identifier: Identifier::unchecked(label.into_inner()),
54 labels: labels.collect(),
55 body: self.body,
56 };
57
58 JsonNode::Map(block.into_json_nodes())
59 }
60 None => JsonNode::Body(vec![self.body]),
61 };
62
63 std::iter::once((self.identifier.into_inner(), node)).collect()
64 }
65}
66
67pub(crate) enum JsonNode {
68 Map(Map<String, JsonNode>),
69 Body(Vec<Body>),
70 Expr(Expression),
71}
72
73impl From<JsonNode> for Expression {
74 fn from(node: JsonNode) -> Self {
75 match node {
76 JsonNode::Map(map) => Expression::from_iter(map),
77 JsonNode::Body(mut vec) => {
78 if vec.len() == 1 {
86 vec.remove(0).into()
87 } else {
88 vec.into()
89 }
90 }
91 JsonNode::Expr(expr) => expr,
92 }
93 }
94}
95
96impl<T> From<T> for Expression
97where
98 T: IntoJsonSpec,
99{
100 fn from(value: T) -> Expression {
101 value.into_json_spec()
102 }
103}
104
105impl<T> From<T> for Value
106where
107 T: IntoJsonSpec,
108{
109 fn from(value: T) -> Value {
110 Value::from(value.into_json_spec())
111 }
112}
113
114impl JsonNode {
115 fn deep_merge_into(self, map: &mut Map<String, JsonNode>, key: String) {
116 match map.entry(key) {
117 Entry::Occupied(o) => o.into_mut().deep_merge(self),
118 Entry::Vacant(v) => {
119 v.insert(self);
120 }
121 }
122 }
123
124 fn deep_merge(&mut self, other: JsonNode) {
125 match (self, other) {
126 (JsonNode::Map(lhs), JsonNode::Map(rhs)) => {
127 for (key, node) in rhs {
128 node.deep_merge_into(lhs, key);
129 }
130 }
131 (JsonNode::Body(lhs), JsonNode::Body(mut rhs)) => {
132 lhs.append(&mut rhs);
133 }
134 (lhs, rhs) => *lhs = rhs,
135 }
136 }
137}