1use crate::Path;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
11#[serde(untagged)]
12pub enum Number {
13 Int(i64),
15 Float(f64),
17}
18
19impl Number {
20 #[inline]
22 pub fn int(v: i64) -> Self {
23 Number::Int(v)
24 }
25
26 #[inline]
28 pub fn float(v: f64) -> Self {
29 Number::Float(v)
30 }
31
32 #[inline]
34 pub fn as_f64(&self) -> f64 {
35 match self {
36 Number::Int(i) => *i as f64,
37 Number::Float(f) => *f,
38 }
39 }
40
41 #[inline]
43 pub fn as_i64(&self) -> i64 {
44 match self {
45 Number::Int(i) => *i,
46 Number::Float(f) => *f as i64,
47 }
48 }
49
50 #[inline]
52 pub fn is_int(&self) -> bool {
53 matches!(self, Number::Int(_))
54 }
55
56 #[inline]
58 pub fn is_float(&self) -> bool {
59 matches!(self, Number::Float(_))
60 }
61}
62
63impl From<i64> for Number {
64 fn from(v: i64) -> Self {
65 Number::Int(v)
66 }
67}
68
69impl From<i32> for Number {
70 fn from(v: i32) -> Self {
71 Number::Int(v as i64)
72 }
73}
74
75impl From<u32> for Number {
76 fn from(v: u32) -> Self {
77 Number::Int(v as i64)
78 }
79}
80
81impl From<u64> for Number {
82 fn from(v: u64) -> Self {
83 Number::Int(v as i64)
84 }
85}
86
87impl From<f64> for Number {
88 fn from(v: f64) -> Self {
89 Number::Float(v)
90 }
91}
92
93impl From<f32> for Number {
94 fn from(v: f32) -> Self {
95 Number::Float(v as f64)
96 }
97}
98
99#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
104#[serde(tag = "op", rename_all = "snake_case")]
105pub enum Op {
106 Set {
111 path: Path,
113 value: Value,
115 },
116
117 Delete {
121 path: Path,
123 },
124
125 Append {
130 path: Path,
132 value: Value,
134 },
135
136 MergeObject {
141 path: Path,
143 value: Value,
145 },
146
147 Increment {
151 path: Path,
153 amount: Number,
155 },
156
157 Decrement {
161 path: Path,
163 amount: Number,
165 },
166
167 Insert {
172 path: Path,
174 index: usize,
176 value: Value,
178 },
179
180 Remove {
185 path: Path,
187 value: Value,
189 },
190
191 LatticeMerge {
196 path: Path,
198 value: Value,
200 },
201}
202
203impl Op {
204 #[inline]
208 pub fn set(path: Path, value: impl Into<Value>) -> Self {
209 Op::Set {
210 path,
211 value: value.into(),
212 }
213 }
214
215 #[inline]
217 pub fn delete(path: Path) -> Self {
218 Op::Delete { path }
219 }
220
221 #[inline]
223 pub fn append(path: Path, value: impl Into<Value>) -> Self {
224 Op::Append {
225 path,
226 value: value.into(),
227 }
228 }
229
230 #[inline]
232 pub fn merge_object(path: Path, value: impl Into<Value>) -> Self {
233 Op::MergeObject {
234 path,
235 value: value.into(),
236 }
237 }
238
239 #[inline]
241 pub fn increment(path: Path, amount: impl Into<Number>) -> Self {
242 Op::Increment {
243 path,
244 amount: amount.into(),
245 }
246 }
247
248 #[inline]
250 pub fn decrement(path: Path, amount: impl Into<Number>) -> Self {
251 Op::Decrement {
252 path,
253 amount: amount.into(),
254 }
255 }
256
257 #[inline]
259 pub fn insert(path: Path, index: usize, value: impl Into<Value>) -> Self {
260 Op::Insert {
261 path,
262 index,
263 value: value.into(),
264 }
265 }
266
267 #[inline]
269 pub fn remove(path: Path, value: impl Into<Value>) -> Self {
270 Op::Remove {
271 path,
272 value: value.into(),
273 }
274 }
275
276 #[inline]
278 pub fn lattice_merge(path: Path, value: impl Into<Value>) -> Self {
279 Op::LatticeMerge {
280 path,
281 value: value.into(),
282 }
283 }
284
285 #[inline]
287 pub fn path(&self) -> &Path {
288 match self {
289 Op::Set { path, .. } => path,
290 Op::Delete { path } => path,
291 Op::Append { path, .. } => path,
292 Op::MergeObject { path, .. } => path,
293 Op::Increment { path, .. } => path,
294 Op::Decrement { path, .. } => path,
295 Op::Insert { path, .. } => path,
296 Op::Remove { path, .. } => path,
297 Op::LatticeMerge { path, .. } => path,
298 }
299 }
300
301 #[inline]
303 pub fn path_mut(&mut self) -> &mut Path {
304 match self {
305 Op::Set { path, .. } => path,
306 Op::Delete { path } => path,
307 Op::Append { path, .. } => path,
308 Op::MergeObject { path, .. } => path,
309 Op::Increment { path, .. } => path,
310 Op::Decrement { path, .. } => path,
311 Op::Insert { path, .. } => path,
312 Op::Remove { path, .. } => path,
313 Op::LatticeMerge { path, .. } => path,
314 }
315 }
316
317 #[inline]
319 pub fn name(&self) -> &'static str {
320 match self {
321 Op::Set { .. } => "set",
322 Op::Delete { .. } => "delete",
323 Op::Append { .. } => "append",
324 Op::MergeObject { .. } => "merge_object",
325 Op::Increment { .. } => "increment",
326 Op::Decrement { .. } => "decrement",
327 Op::Insert { .. } => "insert",
328 Op::Remove { .. } => "remove",
329 Op::LatticeMerge { .. } => "lattice_merge",
330 }
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use crate::path;
338 use serde_json::json;
339
340 #[test]
341 fn test_op_constructors() {
342 let set = Op::set(path!("a"), json!(1));
343 assert_eq!(set.name(), "set");
344 assert_eq!(set.path(), &path!("a"));
345
346 let del = Op::delete(path!("b"));
347 assert_eq!(del.name(), "delete");
348
349 let inc = Op::increment(path!("c"), 5i64);
350 assert_eq!(inc.name(), "increment");
351 }
352
353 #[test]
354 fn test_op_serde() {
355 let op = Op::set(path!("users", 0, "name"), json!("Alice"));
356 let json = serde_json::to_string(&op).unwrap();
357 let parsed: Op = serde_json::from_str(&json).unwrap();
358 assert_eq!(op, parsed);
359 }
360
361 #[test]
362 fn test_number_conversions() {
363 let n: Number = 42i64.into();
364 assert!(n.is_int());
365 assert_eq!(n.as_i64(), 42);
366
367 let n: Number = 1.5f64.into();
368 assert!(n.is_float());
369 assert!((n.as_f64() - 1.5).abs() < 0.001);
370 }
371}