Coverage Report

Created: 2025-09-19 09:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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(&current_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
}