Skip to main content

qubit_redact/facade/redactor/
json.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! JSON redaction operations.
9
10use std::io::Error as IoError;
11use std::io::ErrorKind;
12
13use serde_json::Error as JsonError;
14use serde_json::Value;
15use serde_json::to_writer;
16
17use super::Redactor;
18use super::internal::bounded_json_writer::BoundedJsonWriter;
19use crate::RedactionInspection;
20use crate::RedactionInspectionError;
21use crate::RedactionTextOutput;
22
23impl Redactor {
24    /// Serializes a domain object's redacted view as compact JSON.
25    ///
26    /// This uses domain field declarations, unlike `redact_json`, which parses
27    /// input JSON and classifies its keys. It shares the view's projection and
28    /// logical Serde payload budget, and additionally bounds the final encoded
29    /// JSON by `max_output_bytes`, including labels, framing, and escaping.
30    /// Serialization runs once; no partial string is returned on failure.
31    /// With identical source state, outputs match direct view serialization
32    /// when both succeed. The view's fields must support redacted
33    /// serialization.
34    ///
35    /// # Errors
36    ///
37    /// Propagates JSON serializer errors and errors from the structured
38    /// redaction budget. Successful serialization can contain the structured
39    /// runtime's opaque replacements; it is not a completeness assertion.
40    ///
41    /// # Type Parameters
42    ///
43    /// - `'value`: Source borrow retained while serializing the projection.
44    /// - `T`: Possibly unsized source whose borrowed redacted fields implement
45    ///   Serialize.
46    ///
47    /// # Parameters
48    ///
49    /// - `value`: Source borrowed and traversed once through its redacted
50    ///   projection.
51    ///
52    /// # Returns
53    ///
54    /// A complete compact JSON string within the final encoded output limit.
55    #[inline]
56    pub fn to_json<'value, T: crate::domain::internal::RedactSerializeSource + ?Sized>(
57        &self,
58        value: &'value T,
59    ) -> Result<String, JsonError>
60    where
61        T::RedactedFields<'value>: serde::Serialize,
62    {
63        let mut writer = BoundedJsonWriter::new(self.policy().limits().max_output_bytes());
64        to_writer(&mut writer, &self.redact_view(value))?;
65        String::from_utf8(writer.into_bytes().map_err(JsonError::io)?)
66            .map_err(|error| JsonError::io(IoError::new(ErrorKind::InvalidData, error)))
67    }
68
69    /// Redacts JSON text through one completed text transaction.
70    ///
71    /// # Parameters
72    ///
73    /// - `text`: Complete raw JSON input admitted before parsing.
74    ///
75    /// # Returns
76    ///
77    /// Safe bounded text and its completion, provenance, and resource
78    /// accounting.
79    #[must_use]
80    #[inline]
81    pub fn redact_json(&self, text: &str) -> RedactionTextOutput {
82        let mut session = self.text_runtime();
83        session.json(|json| {
84            let _ = json.text(text);
85        });
86        session.finish()
87    }
88
89    /// Redacts a borrowed parsed JSON value without taking ownership of it.
90    ///
91    /// # Parameters
92    ///
93    /// - `value`: Borrowed parsed tree admitted under shared resource limits.
94    ///
95    /// # Returns
96    ///
97    /// Safe bounded text and its completion, provenance, and resource
98    /// accounting.
99    #[must_use]
100    #[inline]
101    pub fn redact_json_value(&self, value: &Value) -> RedactionTextOutput {
102        let mut session = self.text_runtime();
103        session.json(|json| {
104            let _ = json.value(value);
105        });
106        session.finish()
107    }
108
109    /// Inspects one JSON document without rendering it.
110    ///
111    /// # Errors
112    ///
113    /// Returns [`RedactionInspectionError`] when JSON parsing fails or a
114    /// shared resource limit prevents complete inspection.
115    ///
116    /// # Parameters
117    ///
118    /// - `text`: Complete raw JSON input to classify without rendering values.
119    ///
120    /// # Returns
121    ///
122    /// A conclusive sensitivity observation when parsing and traversal
123    /// complete.
124    #[inline]
125    pub fn inspect_json(&self, text: &str) -> Result<RedactionInspection, RedactionInspectionError> {
126        let mut session = self.inspection_runtime();
127        crate::formats::json::inspection::inspect_text(&mut session, text);
128        session.finish()
129    }
130
131    /// Inspects a borrowed parsed JSON value without taking ownership of it.
132    ///
133    /// # Errors
134    ///
135    /// Returns [`RedactionInspectionError`] when a shared structural, value,
136    /// or input limit prevents complete inspection.
137    ///
138    /// # Parameters
139    ///
140    /// - `value`: Borrowed parsed tree to classify without rendering values.
141    ///
142    /// # Returns
143    ///
144    /// A conclusive sensitivity observation when the entire traversal is
145    /// admitted.
146    #[inline]
147    pub fn inspect_json_value(&self, value: &Value) -> Result<RedactionInspection, RedactionInspectionError> {
148        let mut session = self.inspection_runtime();
149        crate::formats::json::inspection::inspect_borrowed_value(&mut session, value);
150        session.finish()
151    }
152}