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}