1use crate::{Number, Op, Patch, Path};
7use serde_json::Value;
8
9#[doc(hidden)]
14pub trait WriterOps {
15 fn ops(&self) -> &[Op];
17
18 fn ops_mut(&mut self) -> &mut Vec<Op>;
20
21 fn take_ops(&mut self) -> Vec<Op>;
23
24 fn into_patch(self) -> Patch;
26}
27
28#[derive(Debug, Clone)]
49pub struct JsonWriter {
50 base: Path,
51 ops: Vec<Op>,
52}
53
54impl JsonWriter {
55 #[inline]
57 pub fn new() -> Self {
58 Self {
59 base: Path::root(),
60 ops: Vec::new(),
61 }
62 }
63
64 #[inline]
66 pub fn at(base: Path) -> Self {
67 Self {
68 base,
69 ops: Vec::new(),
70 }
71 }
72
73 #[inline]
75 pub fn base(&self) -> &Path {
76 &self.base
77 }
78
79 #[inline]
84 pub fn nested(&self, path: Path) -> Self {
85 Self {
86 base: self.base.join(&path),
87 ops: Vec::new(),
88 }
89 }
90
91 #[inline]
93 fn full_path(&self, path: Path) -> Path {
94 if path.is_empty() {
95 self.base.clone()
96 } else {
97 self.base.join(&path)
98 }
99 }
100
101 #[inline]
103 pub fn set(&mut self, path: Path, value: impl Into<Value>) -> &mut Self {
104 self.ops.push(Op::Set {
105 path: self.full_path(path),
106 value: value.into(),
107 });
108 self
109 }
110
111 #[inline]
113 pub fn set_root(&mut self, value: impl Into<Value>) -> &mut Self {
114 self.ops.push(Op::Set {
115 path: self.base.clone(),
116 value: value.into(),
117 });
118 self
119 }
120
121 #[inline]
123 pub fn delete(&mut self, path: Path) -> &mut Self {
124 self.ops.push(Op::Delete {
125 path: self.full_path(path),
126 });
127 self
128 }
129
130 #[inline]
132 pub fn append(&mut self, path: Path, value: impl Into<Value>) -> &mut Self {
133 self.ops.push(Op::Append {
134 path: self.full_path(path),
135 value: value.into(),
136 });
137 self
138 }
139
140 #[inline]
142 pub fn merge_object(&mut self, path: Path, value: impl Into<Value>) -> &mut Self {
143 self.ops.push(Op::MergeObject {
144 path: self.full_path(path),
145 value: value.into(),
146 });
147 self
148 }
149
150 #[inline]
152 pub fn increment(&mut self, path: Path, amount: impl Into<Number>) -> &mut Self {
153 self.ops.push(Op::Increment {
154 path: self.full_path(path),
155 amount: amount.into(),
156 });
157 self
158 }
159
160 #[inline]
162 pub fn decrement(&mut self, path: Path, amount: impl Into<Number>) -> &mut Self {
163 self.ops.push(Op::Decrement {
164 path: self.full_path(path),
165 amount: amount.into(),
166 });
167 self
168 }
169
170 #[inline]
172 pub fn insert(&mut self, path: Path, index: usize, value: impl Into<Value>) -> &mut Self {
173 self.ops.push(Op::Insert {
174 path: self.full_path(path),
175 index,
176 value: value.into(),
177 });
178 self
179 }
180
181 #[inline]
183 pub fn remove(&mut self, path: Path, value: impl Into<Value>) -> &mut Self {
184 self.ops.push(Op::Remove {
185 path: self.full_path(path),
186 value: value.into(),
187 });
188 self
189 }
190
191 #[inline]
193 pub fn merge<W: WriterOps>(&mut self, mut other: W) -> &mut Self {
194 self.ops.extend(other.take_ops());
195 self
196 }
197
198 #[inline]
200 pub fn build(self) -> Patch {
201 Patch::with_ops(self.ops)
202 }
203
204 #[inline]
206 pub fn is_empty(&self) -> bool {
207 self.ops.is_empty()
208 }
209
210 #[inline]
212 pub fn len(&self) -> usize {
213 self.ops.len()
214 }
215
216 #[inline]
218 pub fn clear(&mut self) {
219 self.ops.clear();
220 }
221}
222
223impl Default for JsonWriter {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229impl WriterOps for JsonWriter {
230 fn ops(&self) -> &[Op] {
231 &self.ops
232 }
233
234 fn ops_mut(&mut self) -> &mut Vec<Op> {
235 &mut self.ops
236 }
237
238 fn take_ops(&mut self) -> Vec<Op> {
239 std::mem::take(&mut self.ops)
240 }
241
242 fn into_patch(self) -> Patch {
243 self.build()
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::path;
251 use serde_json::json;
252
253 #[test]
254 fn test_json_writer_basic() {
255 let mut w = JsonWriter::new();
256 w.set(path!("name"), json!("Alice"));
257 w.set(path!("age"), json!(30));
258
259 let patch = w.build();
260 assert_eq!(patch.len(), 2);
261 }
262
263 #[test]
264 fn test_json_writer_with_base() {
265 let mut w = JsonWriter::at(path!("user"));
266 w.set(path!("name"), json!("Bob"));
267
268 let patch = w.build();
269 assert_eq!(patch.ops()[0].path(), &path!("user", "name"));
270 }
271
272 #[test]
273 fn test_json_writer_nested() {
274 let w = JsonWriter::at(path!("data"));
275 let nested = w.nested(path!("items"));
276 assert_eq!(nested.base(), &path!("data", "items"));
277 }
278
279 #[test]
280 fn test_json_writer_merge() {
281 let mut w1 = JsonWriter::new();
282 w1.set(path!("a"), json!(1));
283
284 let mut w2 = JsonWriter::new();
285 w2.set(path!("b"), json!(2));
286
287 w1.merge(w2);
288 assert_eq!(w1.len(), 2);
289 }
290
291 #[test]
292 fn test_json_writer_all_ops() {
293 let mut w = JsonWriter::new();
294
295 w.set(path!("x"), json!(1));
296 w.delete(path!("y"));
297 w.append(path!("arr"), json!(1));
298 w.merge_object(path!("obj"), json!({"a": 1}));
299 w.increment(path!("count"), 1i64);
300 w.decrement(path!("score"), 5i64);
301 w.insert(path!("list"), 0, json!("first"));
302 w.remove(path!("tags"), json!("old"));
303
304 assert_eq!(w.len(), 8);
305 }
306}