1use std::sync::Arc;
2
3use super::{ConfigSource, Hierarchical};
4use crate::value::{FileFormat, Map, Pointer, Value, ValueOrigin, WithOrigin};
5
6#[derive(Debug, Clone)]
8pub struct Json {
9 origin: Arc<ValueOrigin>,
10 inner: WithOrigin,
11}
12
13impl Json {
14 pub fn empty(filename: &str) -> Self {
16 Self::new(filename, serde_json::Map::default())
17 }
18
19 pub fn new(filename: &str, object: serde_json::Map<String, serde_json::Value>) -> Self {
21 let origin = Arc::new(ValueOrigin::File {
22 name: filename.to_owned(),
23 format: FileFormat::Json,
24 });
25 let inner = Self::map_value(serde_json::Value::Object(object), &origin, String::new());
26 Self { origin, inner }
27 }
28
29 pub fn merge(&mut self, at: &str, value: impl serde::Serialize) {
39 let value = serde_json::to_value(value).expect("failed serializing inserted value");
40 assert!(
41 !at.is_empty() || value.is_object(),
42 "Cannot overwrite root object"
43 );
44
45 let value = Self::map_value(value, &self.origin, at.to_owned());
46
47 let merge_point = if let Some((parent, last_segment)) = Pointer(at).split_last() {
48 self.inner.ensure_object(parent, |path| {
49 Arc::new(ValueOrigin::Path {
50 source: self.origin.clone(),
51 path: path.0.to_owned(),
52 })
53 });
54
55 let parent = self.inner.get_mut(parent).unwrap();
57 let Value::Object(parent_object) = &mut parent.inner else {
58 unreachable!();
59 };
60 if !parent_object.contains_key(last_segment) {
61 parent_object.insert(
62 last_segment.to_owned(),
63 WithOrigin {
64 inner: Value::Null,
65 origin: Arc::default(), },
67 );
68 }
69 parent_object.get_mut(last_segment).unwrap()
70 } else {
71 &mut self.inner
72 };
73
74 merge_point.deep_merge(value);
75 debug_assert!(matches!(&self.inner.inner, Value::Object(_)));
76 }
77
78 pub(crate) fn map_value(
79 value: serde_json::Value,
80 file_origin: &Arc<ValueOrigin>,
81 path: String,
82 ) -> WithOrigin {
83 let inner = match value {
84 serde_json::Value::Bool(value) => value.into(),
85 serde_json::Value::Number(value) => value.into(),
86 serde_json::Value::String(value) => value.into(),
87 serde_json::Value::Null => Value::Null,
88 serde_json::Value::Array(values) => Value::Array(
89 values
90 .into_iter()
91 .enumerate()
92 .map(|(i, value)| {
93 let child_path = Pointer(&path).join(&i.to_string());
94 Self::map_value(value, file_origin, child_path)
95 })
96 .collect(),
97 ),
98 serde_json::Value::Object(values) => Value::Object(
99 values
100 .into_iter()
101 .map(|(key, value)| {
102 let value = Self::map_value(value, file_origin, Pointer(&path).join(&key));
103 (key, value)
104 })
105 .collect(),
106 ),
107 };
108
109 WithOrigin {
110 inner,
111 origin: if path.is_empty() {
112 file_origin.clone()
113 } else {
114 Arc::new(ValueOrigin::Path {
115 source: file_origin.clone(),
116 path,
117 })
118 },
119 }
120 }
121
122 #[cfg(test)]
123 pub(crate) fn inner(&self) -> &WithOrigin {
124 &self.inner
125 }
126}
127
128impl ConfigSource for Json {
129 type Kind = Hierarchical;
130
131 fn into_contents(self) -> WithOrigin<Map> {
132 self.inner.map(|value| match value {
133 Value::Object(map) => map,
134 _ => Map::default(),
135 })
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use assert_matches::assert_matches;
142
143 use super::*;
144 use crate::{testonly::extract_json_name, value::StrValue};
145
146 #[test]
147 fn creating_json_config() {
148 let json = serde_json::json!({
149 "bool_value": true,
150 "nested": {
151 "int_value": 123,
152 "str": "???",
153 },
154 });
155 let serde_json::Value::Object(json) = json else {
156 unreachable!();
157 };
158 let mut json = Json::new("test.json", json);
159
160 let bool_value = json.inner.get(Pointer("bool_value")).unwrap();
161 assert_matches!(bool_value.inner, Value::Bool(true));
162 assert_matches!(
163 bool_value.origin.as_ref(),
164 ValueOrigin::Path { path, source } if path == "bool_value" && extract_json_name(source) == "test.json"
165 );
166
167 let str = json.inner.get(Pointer("nested.str")).unwrap();
168 assert_matches!(&str.inner, Value::String(StrValue::Plain(s)) if s == "???");
169 assert_matches!(
170 str.origin.as_ref(),
171 ValueOrigin::Path { path, source } if path == "nested.str" && extract_json_name(source) == "test.json"
172 );
173
174 json.merge("nested.str", "!!!");
175 let str = json.inner.get(Pointer("nested.str")).unwrap();
176 assert_matches!(&str.inner, Value::String(StrValue::Plain(s)) if s == "!!!");
177
178 json.merge(
179 "nested",
180 serde_json::json!({
181 "int_value": 5,
182 "array": [1, 2],
183 }),
184 );
185 let str = json.inner.get(Pointer("nested.str")).unwrap();
186 assert_matches!(&str.inner, Value::String(StrValue::Plain(s)) if s == "!!!");
187 let int_value = json.inner.get(Pointer("nested.int_value")).unwrap();
188 assert_matches!(&int_value.inner, Value::Number(num) if *num == 5_u64.into());
189 let array = json.inner.get(Pointer("nested.array")).unwrap();
190 assert_matches!(&array.inner, Value::Array(items) if items.len() == 2);
191 }
192
193 #[test]
194 fn creating_config_using_macro() {
195 let json = config! {
196 "bool_value": true,
197 "nested.str": "???",
198 "nested.int_value": 123,
199 };
200
201 let bool_value = json.inner.get(Pointer("bool_value")).unwrap();
202 assert_matches!(bool_value.inner, Value::Bool(true));
203 assert_matches!(
204 bool_value.origin.as_ref(),
205 ValueOrigin::Path { path, source }
206 if path == "bool_value" && extract_json_name(source).contains("inline config")
207 );
208
209 let str = json.inner.get(Pointer("nested.str")).unwrap();
210 assert_matches!(&str.inner, Value::String(StrValue::Plain(s)) if s == "???");
211 assert_matches!(
212 str.origin.as_ref(),
213 ValueOrigin::Path { path, .. } if path == "nested.str"
214 );
215 }
216}