qubit_redact/facade/redacted_view.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Borrowed diagnostic views carrying an immutable policy snapshot.
9
10use std::fmt;
11
12#[cfg(feature = "serde")]
13use serde::Serialize;
14#[cfg(feature = "serde")]
15use serde::Serializer;
16
17use crate::Redact;
18use crate::Redactor;
19#[cfg(feature = "serde")]
20use crate::domain::internal::RedactSerializeSource;
21#[cfg(feature = "serde")]
22use crate::domain::internal::RedactedProjectionRef;
23
24/// A lazy view of a source value under one fixed redaction policy.
25///
26/// Creation does not inspect or format the source. Each formatting or Serde
27/// call starts a fresh execution and budget, reading the source again. The
28/// policy is fixed, but interior-mutable source values are not snapshotted.
29/// This is neither a modified business object nor cached redacted output.
30/// Use [`Redactor::redact_text`] when a finalized text and summary are needed.
31///
32/// With the `serde` feature, views of derived values serialize structurally
33/// through their redacted field projections. The source's ordinary `Serialize`
34/// implementation is not used as the root redaction entry point.
35/// Direct Serde observes logical payload limits; an external serializer owns
36/// its final encoded size. `Redactor::to_json` also bounds final JSON bytes.
37///
38/// # Type Parameters
39///
40/// - `'value`: Lifetime of the borrowed source.
41/// - `T`: Source type; formatting/serialization capabilities are required on
42/// use.
43///
44/// # Examples
45///
46/// ```
47/// use qubit_redact::{Redact, RedactionWriter, Redactor};
48/// struct Event;
49/// impl Redact for Event {
50/// fn write_redacted(&self, writer: &mut RedactionWriter<'_>) {
51/// writer.record("Event", |fields| {
52/// fields.unmarked("status", || "ready");
53/// });
54/// }
55/// }
56/// let event = Event;
57/// let redactor = Redactor::standard();
58/// let view = redactor.redact_view(&event);
59/// assert_eq!(format!("{view}"), redactor.redact_text(&event).text().as_str());
60/// ```
61pub struct RedactedView<'value, T: ?Sized> {
62 /// Source borrowed until the view is dropped.
63 value: &'value T,
64 /// Owned policy snapshot independent of the application default.
65 redactor: Redactor,
66}
67
68impl<'value, T: ?Sized> RedactedView<'value, T> {
69 /// Captures `redactor` without reading or formatting `value`.
70 ///
71 /// # Parameters
72 ///
73 /// - `value`: Source retained by reference without evaluation.
74 /// - `redactor`: Owned immutable policy snapshot used for every later
75 /// execution.
76 ///
77 /// # Returns
78 ///
79 /// A lazy view retaining both source borrow and policy snapshot.
80 #[must_use]
81 #[inline(always)]
82 pub(crate) fn new(value: &'value T, redactor: Redactor) -> Self {
83 Self { value, redactor }
84 }
85}
86
87impl<T: Redact + ?Sized> fmt::Display for RedactedView<'_, T> {
88 /// Renders the source once through a fresh text transaction.
89 ///
90 /// # Parameters
91 ///
92 /// - `formatter`: Destination receiving the redacted text of a fresh
93 /// execution.
94 ///
95 /// # Returns
96 ///
97 /// Success after publishing the rendered text.
98 ///
99 /// # Errors
100 ///
101 /// Propagates a destination formatting error; redaction failures remain
102 /// safe output metadata.
103 #[inline(always)]
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 formatter.write_str(self.redactor.redact_text(self.value).text().as_str())
106 }
107}
108
109impl<T: Redact + ?Sized> fmt::Debug for RedactedView<'_, T> {
110 /// Shows redacted text without exposing wrapper internals or source Debug.
111 ///
112 /// # Parameters
113 ///
114 /// - `formatter`: Destination receiving the redacted text of a fresh
115 /// execution.
116 ///
117 /// # Returns
118 ///
119 /// Success after publishing the rendered text.
120 ///
121 /// # Errors
122 ///
123 /// Propagates a destination formatting error; redaction failures remain
124 /// safe output metadata.
125 #[inline(always)]
126 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
127 fmt::Display::fmt(self, formatter)
128 }
129}
130
131#[cfg(feature = "serde")]
132impl<'value, T: RedactSerializeSource + ?Sized> Serialize for RedactedView<'value, T>
133where
134 T::RedactedFields<'value>: Serialize,
135{
136 /// Serializes with this view's policy, propagating serializer/budget
137 /// errors.
138 ///
139 /// # Type Parameters
140 ///
141 /// - `S`: Destination serializer and its result/error types.
142 ///
143 /// # Parameters
144 ///
145 /// - `serializer`: Destination receiving the structured redacted
146 /// projection.
147 ///
148 /// # Returns
149 ///
150 /// The destination result after one policy-scoped structured execution.
151 ///
152 /// # Errors
153 ///
154 /// Returns the serializer’s error for rejected admission or payload
155 /// budgets, or propagates a downstream serialization error.
156 #[inline(always)]
157 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158 where
159 S: Serializer,
160 {
161 RedactedProjectionRef::new(self.value, self.redactor.policy()).serialize(serializer)
162 }
163}