/home/gh-runner/action-runner3/_work/feoxdb/feoxdb/src/core/store/json_patch.rs
Line | Count | Source |
1 | | use crate::error::{FeoxError, Result}; |
2 | | |
3 | | use super::FeoxStore; |
4 | | |
5 | | impl FeoxStore { |
6 | | /// Apply a JSON patch to a value. |
7 | | /// |
8 | | /// Uses RFC 6902 JSON Patch format to modify specific fields in a JSON document. |
9 | | /// Both the existing value and the patch must be valid JSON. |
10 | | /// |
11 | | /// # Arguments |
12 | | /// |
13 | | /// * `key` - The key containing the JSON document to patch |
14 | | /// * `patch` - JSON Patch operations in RFC 6902 format |
15 | | /// * `timestamp` - Optional timestamp for conflict resolution |
16 | | /// |
17 | | /// # Returns |
18 | | /// |
19 | | /// Returns `Ok(())` if the update was applied. |
20 | | /// |
21 | | /// # Errors |
22 | | /// |
23 | | /// * `KeyNotFound` - Key does not exist |
24 | | /// * `OlderTimestamp` - Timestamp is not newer than existing record |
25 | | /// * `JsonPatchError` - Invalid JSON document or patch format |
26 | | /// |
27 | | /// # Example |
28 | | /// |
29 | | /// ```no_run |
30 | | /// # use feoxdb::FeoxStore; |
31 | | /// # fn main() -> feoxdb::Result<()> { |
32 | | /// # let store = FeoxStore::new(None)?; |
33 | | /// // Insert initial JSON value |
34 | | /// let initial = br#"{"name":"Alice","age":30}"#; |
35 | | /// store.insert(b"user:1", initial)?; |
36 | | /// |
37 | | /// // Apply JSON patch to update age |
38 | | /// let patch = br#"[{"op":"replace","path":"/age","value":31}]"#; |
39 | | /// store.json_patch(b"user:1", patch)?; |
40 | | /// |
41 | | /// // Value now has age updated to 31 |
42 | | /// let updated = store.get(b"user:1")?; |
43 | | /// assert_eq!(updated.len(), initial.len()); // Same length, just age changed |
44 | | /// # Ok(()) |
45 | | /// # } |
46 | | /// ``` |
47 | 2 | pub fn json_patch(&self, key: &[u8], patch: &[u8]) -> Result<()> { |
48 | 2 | self.json_patch_with_timestamp(key, patch, None) |
49 | 2 | } |
50 | | |
51 | | /// Apply JSON patch with explicit timestamp. |
52 | | /// |
53 | | /// This is the advanced version that allows manual timestamp control. |
54 | | /// Most users should use `json_patch()` instead. |
55 | | /// |
56 | | /// # Arguments |
57 | | /// |
58 | | /// * `key` - The key whose value to patch |
59 | | /// * `patch` - JSON Patch array (RFC 6902) |
60 | | /// * `timestamp` - Optional timestamp. If `None`, uses current time. |
61 | | /// |
62 | | /// # Errors |
63 | | /// |
64 | | /// * `OlderTimestamp` - Timestamp is not newer than existing record |
65 | 2 | pub fn json_patch_with_timestamp( |
66 | 2 | &self, |
67 | 2 | key: &[u8], |
68 | 2 | patch: &[u8], |
69 | 2 | timestamp: Option<u64>, |
70 | 2 | ) -> Result<()> { |
71 | 2 | let timestamp = match timestamp { |
72 | 2 | Some(0) | None => self.get_timestamp(), |
73 | 0 | Some(ts) => ts, |
74 | | }; |
75 | 2 | self.validate_key(key)?0 ; |
76 | | |
77 | | // Get the value and release the lock immediately |
78 | 2 | let current_value = { |
79 | 2 | let record = self |
80 | 2 | .hash_table |
81 | 2 | .read(key, |_, v| v.clone()) |
82 | 2 | .ok_or(FeoxError::KeyNotFound)?0 ; |
83 | | |
84 | 2 | if timestamp < record.timestamp { |
85 | 0 | return Err(FeoxError::OlderTimestamp); |
86 | 2 | } |
87 | | |
88 | 2 | if let Some(val) = record.get_value() { |
89 | 2 | val.to_vec() |
90 | | } else { |
91 | 0 | self.load_value_from_disk(&record)? |
92 | | } |
93 | | }; |
94 | | |
95 | 2 | let new_value1 = crate::utils::json_patch::apply_json_patch(¤t_value, patch)?1 ; |
96 | | |
97 | | // Now update without holding any references |
98 | 1 | self.insert_with_timestamp(key, &new_value, Some(timestamp))?0 ; |
99 | 1 | Ok(()) |
100 | 2 | } |
101 | | } |