use serde::{Serialize, Serializer, ser::{SerializeMap, SerializeSeq}};
pub enum JsonKind<'a> {
Null,
Bool(bool),
String(&'a str),
Number(&'a str),
Array,
Object,
}
pub trait ContextJson {
fn kind(&self, index: usize) -> JsonKind<'_>;
fn child(&self, parent: usize, after: Option<usize>) -> Option<(Option<&str>, usize)>;
}
pub trait ContextSource: Send + Sync {
fn release(self: std::sync::Arc<Self>) { drop(self); }
fn with_tree(&self, visitor: &mut dyn FnMut(JsonSubtree<'_>));
fn secret_paths(&self) -> &[String] { &[] }
fn with_tree_and_secrets(&self, visitor: &mut dyn FnMut(JsonSubtree<'_>, &[String])) {
self.with_tree(&mut |tree| visitor(tree, self.secret_paths()));
}
fn with_secrets(&self, visitor: &mut dyn FnMut(&[String])) { visitor(self.secret_paths()); }
}
pub struct ContextSourceRef(Option<std::sync::Arc<dyn ContextSource>>);
impl ContextSourceRef {
pub fn new<T: ContextSource + 'static>(source: std::sync::Arc<T>) -> Self { Self(Some(source)) }
}
impl From<std::sync::Arc<dyn ContextSource>> for ContextSourceRef {
fn from(source: std::sync::Arc<dyn ContextSource>) -> Self { Self(Some(source)) }
}
impl<T: ContextSource + 'static> From<std::sync::Arc<T>> for ContextSourceRef {
fn from(source: std::sync::Arc<T>) -> Self { Self::new(source) }
}
impl AsRef<dyn ContextSource> for ContextSourceRef {
fn as_ref(&self) -> &(dyn ContextSource + 'static) { self.0.as_deref().expect("live source") }
}
impl Clone for ContextSourceRef {
fn clone(&self) -> Self { Self(self.0.clone()) }
}
impl std::ops::Deref for ContextSourceRef {
type Target = dyn ContextSource;
fn deref(&self) -> &Self::Target { self.0.as_deref().expect("live source") }
}
impl Drop for ContextSourceRef {
fn drop(&mut self) { if let Some(source) = self.0.take() { source.release(); } }
}
#[derive(Clone, Copy)]
pub struct JsonSubtree<'a> {
owner: &'a dyn ContextJson,
index: usize,
}
impl<'a> JsonSubtree<'a> {
pub fn same_node(self, other: Self) -> bool {
std::ptr::eq(self.owner, other.owner) && self.index == other.index
}
pub fn new(owner: &'a dyn ContextJson, index: usize) -> Self { Self { owner, index } }
pub fn kind(self) -> JsonKind<'a> { self.owner.kind(self.index) }
pub fn children(self) -> impl Iterator<Item = (Option<&'a str>, Self)> {
let mut previous = None;
std::iter::from_fn(move || {
let (key, index) = self.owner.child(self.index, previous)?;
previous = Some(index);
Some((key, Self::new(self.owner, index)))
})
}
pub fn pointer(self, path: &str) -> Result<Option<Self>, crate::json_pointer::InvalidJsonPointer> {
let mut current = self;
for segment in crate::json_pointer::JsonPointer::parse(path)?.segments() {
let next = match current.kind() {
JsonKind::Object => current.children()
.find(|(key, _)| key.is_some_and(|key| segment.matches(key))).map(|(_, child)| child),
JsonKind::Array => segment.array_index()
.and_then(|index| current.children().nth(index)).map(|(_, child)| child),
_ => None,
};
let Some(next) = next else { return Ok(None); };
current = next;
}
Ok(Some(current))
}
}
impl Serialize for JsonSubtree<'_> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self.kind() {
JsonKind::Null => serializer.serialize_none(),
JsonKind::Bool(value) => serializer.serialize_bool(value),
JsonKind::String(value) => serializer.serialize_str(value),
JsonKind::Number(value) => {
let raw: &serde_json::value::RawValue = serde_json::from_str(value)
.map_err(serde::ser::Error::custom)?;
raw.serialize(serializer)
}
JsonKind::Array => {
let mut seq = serializer.serialize_seq(None)?;
for (_, child) in self.children() { seq.serialize_element(&child)?; }
seq.end()
}
JsonKind::Object => {
let mut map = serializer.serialize_map(None)?;
for (key, child) in self.children() {
map.serialize_entry(key.ok_or_else(|| serde::ser::Error::custom("object key absent"))?, &child)?;
}
map.end()
}
}
}
}
pub struct RedactedSubtree<'a> {
root: JsonSubtree<'a>,
node: JsonSubtree<'a>,
paths: &'a [String],
prefix: &'a str,
masked: bool,
}
impl<'a> RedactedSubtree<'a> {
pub fn new(root: JsonSubtree<'a>, paths: &'a [String], prefix: &'a str) -> Self {
Self { root, node: root, paths, prefix, masked: false }
}
pub fn pointer(&self, path: &str) -> Result<Option<Self>, crate::json_pointer::InvalidJsonPointer> {
let pointer = crate::json_pointer::JsonPointer::parse(path)?;
let is_secret = |node: JsonSubtree<'a>| self.paths.iter().any(|secret| {
secret.strip_prefix(self.prefix).and_then(|path| self.root.pointer(path).ok().flatten())
.is_some_and(|target| target.same_node(node))
});
let mut node = self.node;
let mut masked = self.masked || is_secret(node);
for segment in pointer.segments() {
let next = match node.kind() {
JsonKind::Object => node.children().find(|(key, _)| key.is_some_and(|key| segment.matches(key))).map(|(_, child)| child),
JsonKind::Array => segment.array_index().and_then(|index| node.children().nth(index)).map(|(_, child)| child),
_ => None,
};
let Some(next) = next else { return Ok(None); };
node = next;
masked |= is_secret(node);
}
Ok(Some(Self { node, masked, ..*self }))
}
fn child(&self, node: JsonSubtree<'a>) -> Self { Self { node, ..*self } }
}
impl Serialize for RedactedSubtree<'_> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let secret = self.paths.iter().any(|path| {
path.strip_prefix(self.prefix).and_then(|path| self.root.pointer(path).ok().flatten())
.is_some_and(|target| target.same_node(self.node))
});
if self.masked || secret { return s.serialize_str("<redacted>"); }
match self.node.kind() {
JsonKind::Object => {
let mut map = s.serialize_map(None)?;
for (key, child) in self.node.children() {
map.serialize_entry(key.ok_or_else(|| serde::ser::Error::custom("object key absent"))?, &self.child(child))?;
}
map.end()
}
JsonKind::Array => {
let mut seq = s.serialize_seq(None)?;
for (_, child) in self.node.children() { seq.serialize_element(&self.child(child))?; }
seq.end()
}
_ => self.node.serialize(s),
}
}
}