Skip to main content

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}