qubit_redact/domain/redacted_keyed_value.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//! Lazy redaction view selected by an external field key.
9
10use std::{
11 fmt::Write as _,
12 fmt::{
13 self,
14 Debug,
15 Display,
16 Formatter,
17 },
18};
19
20use crate::{
21 Redact,
22 RedactValue,
23 RedactionPolicy,
24 text::internal::LogEscapeWriter,
25};
26
27/// A borrowed value rendered according to a separate field key.
28///
29/// The key is used only to select a policy rule. The view itself renders or
30/// serializes the value, and borrows an immutable policy snapshot.
31///
32/// # Type Parameters
33///
34/// * `'value` - Lifetime of the borrowed key and value.
35/// * `'policy` - Lifetime of the borrowed policy snapshot.
36/// * `T` - Value type rendered or serialized through redaction.
37#[must_use = "format or serialize the keyed redaction view"]
38pub struct RedactedKeyedValue<'value, 'policy, T: ?Sized> {
39 /// Field name used for policy classification.
40 key: &'value str,
41 /// Value represented by this view.
42 value: &'value T,
43 /// Immutable policy snapshot borrowed by every output protocol.
44 policy: &'policy RedactionPolicy,
45}
46
47impl<'value, 'policy, T: ?Sized> RedactedKeyedValue<'value, 'policy, T> {
48 /// Creates a keyed view from borrowed inputs and a borrowed policy
49 /// snapshot.
50 ///
51 /// # Parameters
52 ///
53 /// * `key` - Field name used only for policy classification.
54 /// * `value` - Value to render or serialize lazily.
55 /// * `policy` - Complete policy snapshot borrowed by this view.
56 ///
57 /// # Returns
58 ///
59 /// A view that never modifies the original value.
60 #[must_use = "format or serialize the keyed redaction view"]
61 #[inline(always)]
62 pub const fn new(
63 key: &'value str,
64 value: &'value T,
65 policy: &'policy RedactionPolicy,
66 ) -> Self {
67 Self { key, value, policy }
68 }
69
70 /// Returns the external field key used by this view.
71 ///
72 /// # Returns
73 ///
74 /// The unchanged policy lookup key.
75 #[must_use]
76 #[inline(always)]
77 pub const fn key(&self) -> &'value str {
78 self.key
79 }
80}
81
82impl<T: Redact + RedactValue + ?Sized> Debug for RedactedKeyedValue<'_, '_, T> {
83 /// Formats the value through its selected field classification.
84 ///
85 /// # Parameters
86 ///
87 /// * `formatter` - Destination formatter whose flags are preserved.
88 ///
89 /// # Returns
90 ///
91 /// The complete redacted debug result.
92 ///
93 /// # Errors
94 ///
95 /// Returns [`fmt::Error`] when the destination formatter cannot accept the
96 /// complete representation.
97 #[inline]
98 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
99 match self.policy.sensitivity_for(self.key) {
100 Some(level) => Debug::fmt(
101 &self.value.redact_value(level, self.policy.masking()),
102 formatter,
103 ),
104 None => self.value.fmt_redacted(self.policy, formatter),
105 }
106 }
107}
108
109impl<T: Redact + RedactValue + ?Sized> Display
110 for RedactedKeyedValue<'_, '_, T>
111{
112 /// Formats the selected redacted representation for a plain-text log.
113 ///
114 /// # Parameters
115 ///
116 /// * `formatter` - Destination plain-text log boundary.
117 ///
118 /// # Returns
119 ///
120 /// The complete escaped redacted representation.
121 ///
122 /// # Errors
123 ///
124 /// Returns [`fmt::Error`] when the destination formatter cannot accept the
125 /// complete escaped representation.
126 #[inline]
127 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
128 let mut writer = LogEscapeWriter::new(formatter);
129 write!(&mut writer, "{self:?}")
130 }
131}
132
133#[cfg(feature = "serde")]
134impl<T: RedactValue + crate::domain::RedactSerialize + ?Sized> serde::Serialize
135 for RedactedKeyedValue<'_, '_, T>
136{
137 /// Serializes the value through its selected field classification.
138 ///
139 /// # Type Parameters
140 ///
141 /// * `S` - Destination Serde serializer type.
142 ///
143 /// # Parameters
144 ///
145 /// * `serializer` - Destination serde serializer.
146 ///
147 /// # Returns
148 ///
149 /// The serializer's successful redacted output.
150 ///
151 /// # Errors
152 ///
153 /// Returns the destination serializer's error unchanged.
154 #[inline]
155 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
156 where
157 S: serde::Serializer,
158 {
159 match self.policy.sensitivity_for(self.key) {
160 Some(level) => serde::Serialize::serialize(
161 &self.value.redact_value(level, self.policy.masking()),
162 serializer,
163 ),
164 None => crate::domain::RedactSerialize::serialize_redacted(
165 self.value,
166 self.policy,
167 serializer,
168 ),
169 }
170 }
171}