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