feoxdb/core/store/
json_patch.rs

1use crate::error::{FeoxError, Result};
2
3use super::FeoxStore;
4
5impl 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    pub fn json_patch(&self, key: &[u8], patch: &[u8]) -> Result<()> {
48        self.json_patch_with_timestamp(key, patch, None)
49    }
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    pub fn json_patch_with_timestamp(
66        &self,
67        key: &[u8],
68        patch: &[u8],
69        timestamp: Option<u64>,
70    ) -> Result<()> {
71        let timestamp = match timestamp {
72            Some(0) | None => self.get_timestamp(),
73            Some(ts) => ts,
74        };
75        self.validate_key(key)?;
76
77        // Get the value and release the lock immediately
78        let current_value = {
79            let record = self
80                .hash_table
81                .read(key, |_, v| v.clone())
82                .ok_or(FeoxError::KeyNotFound)?;
83
84            if timestamp < record.timestamp {
85                return Err(FeoxError::OlderTimestamp);
86            }
87
88            if let Some(val) = record.get_value() {
89                val.to_vec()
90            } else {
91                self.load_value_from_disk(&record)?
92            }
93        };
94
95        let new_value = crate::utils::json_patch::apply_json_patch(&current_value, patch)?;
96
97        // Now update without holding any references
98        self.insert_with_timestamp(key, &new_value, Some(timestamp))?;
99        Ok(())
100    }
101}