Skip to main content

saddle_core/
context_json.rs

1//! Borrowed JSON contract for context owners. Ownership and allocation bills
2//! stay with the provider; observing a subtree grants no mutation capability.
3use serde::{Serialize, Serializer, ser::{SerializeMap, SerializeSeq}};
4
5pub enum JsonKind<'a> {
6    Null,
7    Bool(bool),
8    String(&'a str),
9    Number(&'a str),
10    Array,
11    Object,
12}
13
14/// Implementations expose a validated immutable tree using owner-local indices.
15/// Numbers are original JSON tokens. Object children carry decoded keys;
16/// array children carry no key. `after` is the previously returned child index.
17pub trait ContextJson {
18    fn kind(&self, index: usize) -> JsonKind<'_>;
19    fn child(&self, parent: usize, after: Option<usize>) -> Option<(Option<&str>, usize)>;
20}
21
22/// Shared immutable provider. The callback runs synchronously under the
23/// provider's read guard; no tree borrow or mutable owner can escape it.
24pub trait ContextSource: Send + Sync {
25    /// Providers with prepaid shared storage release the physical Arc before
26    /// destroying its allocation bill. Every erased handle calls this hook.
27    fn release(self: std::sync::Arc<Self>) { drop(self); }
28    fn with_tree(&self, visitor: &mut dyn FnMut(JsonSubtree<'_>));
29    /// Absolute schema paths explicitly marked secret by the source owner.
30    fn secret_paths(&self) -> &[String] { &[] }
31    /// Borrow a tree and its policy under the same provider read guard.
32    /// Switching Live/Frozen sources override this callback; borrows cannot
33    /// escape the callback or outlive the source's synchronized state.
34    fn with_tree_and_secrets(&self, visitor: &mut dyn FnMut(JsonSubtree<'_>, &[String])) {
35        self.with_tree(&mut |tree| visitor(tree, self.secret_paths()));
36    }
37    fn with_secrets(&self, visitor: &mut dyn FnMut(&[String])) { visitor(self.secret_paths()); }
38}
39
40/// Shared provider reference whose final release remains provider-controlled.
41pub struct ContextSourceRef(Option<std::sync::Arc<dyn ContextSource>>);
42impl ContextSourceRef {
43    pub fn new<T: ContextSource + 'static>(source: std::sync::Arc<T>) -> Self { Self(Some(source)) }
44}
45impl From<std::sync::Arc<dyn ContextSource>> for ContextSourceRef {
46    fn from(source: std::sync::Arc<dyn ContextSource>) -> Self { Self(Some(source)) }
47}
48impl<T: ContextSource + 'static> From<std::sync::Arc<T>> for ContextSourceRef {
49    fn from(source: std::sync::Arc<T>) -> Self { Self::new(source) }
50}
51impl AsRef<dyn ContextSource> for ContextSourceRef {
52    fn as_ref(&self) -> &(dyn ContextSource + 'static) { self.0.as_deref().expect("live source") }
53}
54impl Clone for ContextSourceRef {
55    fn clone(&self) -> Self { Self(self.0.clone()) }
56}
57impl std::ops::Deref for ContextSourceRef {
58    type Target = dyn ContextSource;
59    fn deref(&self) -> &Self::Target { self.0.as_deref().expect("live source") }
60}
61impl Drop for ContextSourceRef {
62    fn drop(&mut self) { if let Some(source) = self.0.take() { source.release(); } }
63}
64
65#[derive(Clone, Copy)]
66pub struct JsonSubtree<'a> {
67    owner: &'a dyn ContextJson,
68    index: usize,
69}
70impl<'a> JsonSubtree<'a> {
71    pub fn same_node(self, other: Self) -> bool {
72        std::ptr::eq(self.owner, other.owner) && self.index == other.index
73    }
74    pub fn new(owner: &'a dyn ContextJson, index: usize) -> Self { Self { owner, index } }
75    pub fn kind(self) -> JsonKind<'a> { self.owner.kind(self.index) }
76    pub fn children(self) -> impl Iterator<Item = (Option<&'a str>, Self)> {
77        let mut previous = None;
78        std::iter::from_fn(move || {
79            let (key, index) = self.owner.child(self.index, previous)?;
80            previous = Some(index);
81            Some((key, Self::new(self.owner, index)))
82        })
83    }
84    pub fn pointer(self, path: &str) -> Result<Option<Self>, crate::json_pointer::InvalidJsonPointer> {
85        let mut current = self;
86        for segment in crate::json_pointer::JsonPointer::parse(path)?.segments() {
87            let next = match current.kind() {
88                JsonKind::Object => current.children()
89                    .find(|(key, _)| key.is_some_and(|key| segment.matches(key))).map(|(_, child)| child),
90                JsonKind::Array => segment.array_index()
91                    .and_then(|index| current.children().nth(index)).map(|(_, child)| child),
92                _ => None,
93            };
94            let Some(next) = next else { return Ok(None); };
95            current = next;
96        }
97        Ok(Some(current))
98    }
99}
100impl Serialize for JsonSubtree<'_> {
101    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
102        match self.kind() {
103            JsonKind::Null => serializer.serialize_none(),
104            JsonKind::Bool(value) => serializer.serialize_bool(value),
105            JsonKind::String(value) => serializer.serialize_str(value),
106            JsonKind::Number(value) => {
107                let raw: &serde_json::value::RawValue = serde_json::from_str(value)
108                    .map_err(serde::ser::Error::custom)?;
109                raw.serialize(serializer)
110            }
111            JsonKind::Array => {
112                let mut seq = serializer.serialize_seq(None)?;
113                for (_, child) in self.children() { seq.serialize_element(&child)?; }
114                seq.end()
115            }
116            JsonKind::Object => {
117                let mut map = serializer.serialize_map(None)?;
118                for (key, child) in self.children() {
119                    map.serialize_entry(key.ok_or_else(|| serde::ser::Error::custom("object key absent"))?, &child)?;
120                }
121                map.end()
122            }
123        }
124    }
125}
126
127/// Explicit source policy, applied before any output field selection.
128pub struct RedactedSubtree<'a> {
129    root: JsonSubtree<'a>,
130    node: JsonSubtree<'a>,
131    paths: &'a [String],
132    prefix: &'a str,
133    masked: bool,
134}
135impl<'a> RedactedSubtree<'a> {
136    pub fn new(root: JsonSubtree<'a>, paths: &'a [String], prefix: &'a str) -> Self {
137        Self { root, node: root, paths, prefix, masked: false }
138    }
139    /// Select only from the protected view. Descendants of a secret source
140    /// remain a placeholder rather than exposing raw values through selectors.
141    pub fn pointer(&self, path: &str) -> Result<Option<Self>, crate::json_pointer::InvalidJsonPointer> {
142        let pointer = crate::json_pointer::JsonPointer::parse(path)?;
143        let is_secret = |node: JsonSubtree<'a>| self.paths.iter().any(|secret| {
144            secret.strip_prefix(self.prefix).and_then(|path| self.root.pointer(path).ok().flatten())
145                .is_some_and(|target| target.same_node(node))
146        });
147        let mut node = self.node;
148        let mut masked = self.masked || is_secret(node);
149        for segment in pointer.segments() {
150            let next = match node.kind() {
151                JsonKind::Object => node.children().find(|(key, _)| key.is_some_and(|key| segment.matches(key))).map(|(_, child)| child),
152                JsonKind::Array => segment.array_index().and_then(|index| node.children().nth(index)).map(|(_, child)| child),
153                _ => None,
154            };
155            let Some(next) = next else { return Ok(None); };
156            node = next;
157            masked |= is_secret(node);
158        }
159        Ok(Some(Self { node, masked, ..*self }))
160    }
161    fn child(&self, node: JsonSubtree<'a>) -> Self { Self { node, ..*self } }
162}
163impl Serialize for RedactedSubtree<'_> {
164    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
165        let secret = self.paths.iter().any(|path| {
166            path.strip_prefix(self.prefix).and_then(|path| self.root.pointer(path).ok().flatten())
167                .is_some_and(|target| target.same_node(self.node))
168        });
169        if self.masked || secret { return s.serialize_str("<redacted>"); }
170        match self.node.kind() {
171            JsonKind::Object => {
172                let mut map = s.serialize_map(None)?;
173                for (key, child) in self.node.children() {
174                    map.serialize_entry(key.ok_or_else(|| serde::ser::Error::custom("object key absent"))?, &self.child(child))?;
175                }
176                map.end()
177            }
178            JsonKind::Array => {
179                let mut seq = s.serialize_seq(None)?;
180                for (_, child) in self.node.children() { seq.serialize_element(&self.child(child))?; }
181                seq.end()
182            }
183            _ => self.node.serialize(s),
184        }
185    }
186}