qubit_redact/facade/redacted_text.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//! Final text produced by one redaction operation.
9
10use std::borrow::Cow;
11use std::fmt;
12
13/// Final UTF-8 text produced by a redaction operation under its selected
14/// policy.
15///
16/// The value has crossed the runtime's plain-text presentation boundary. It is
17/// owned and can be rendered with [`std::fmt::Display`] without running another
18/// redaction pass. This guarantee is policy-relative: a disabled policy or an
19/// explicitly unredacted writer operation may deliberately preserve source
20/// content. Callers must not treat this type as proof that the text is
21/// confidential in every policy configuration. Any additional length
22/// restriction belongs to the caller's final logging or presentation sink.
23///
24/// # Examples
25///
26/// ```
27/// use qubit_redact::Redactor;
28///
29/// let output = Redactor::strict().redact_field("password", "raw-secret");
30/// assert_eq!(output.text().as_str(), "<redacted>");
31/// ```
32#[derive(Debug, Clone, PartialEq, Eq, Hash)]
33pub struct RedactedText(
34 /// Owned text that has already crossed the redaction safety boundary.
35 String,
36);
37
38impl RedactedText {
39 /// Creates final text from an already escaped representation.
40 ///
41 /// # Parameters
42 ///
43 /// - `value`: Already escaped text whose policy transformation is complete.
44 ///
45 /// # Returns
46 ///
47 /// An owned final-text wrapper without another escaping or redaction pass.
48 #[must_use]
49 #[inline(always)]
50 pub(crate) fn from_escaped(value: impl Into<Cow<'static, str>>) -> Self {
51 Self(value.into().into_owned())
52 }
53
54 /// Borrows the final redacted text.
55 ///
56 /// # Returns
57 ///
58 /// The finalized UTF-8 text borrowed from this wrapper.
59 #[must_use]
60 #[inline(always)]
61 pub fn as_str(&self) -> &str {
62 &self.0
63 }
64
65 /// Consumes the wrapper and returns its owned text.
66 ///
67 /// # Returns
68 ///
69 /// The owned finalized text without cloning.
70 #[must_use]
71 #[inline(always)]
72 pub fn into_string(self) -> String {
73 self.0
74 }
75}
76
77impl AsRef<str> for RedactedText {
78 /// Borrows the safe text through the standard string-reference contract.
79 ///
80 /// # Returns
81 ///
82 /// The finalized text borrowed under the standard string-reference
83 /// contract.
84 #[inline(always)]
85 fn as_ref(&self) -> &str {
86 self.as_str()
87 }
88}
89
90impl fmt::Display for RedactedText {
91 /// Writes only the finalized safe text to the destination formatter.
92 ///
93 /// # Parameters
94 ///
95 /// - `formatter`: Destination receiving the already finalized text.
96 ///
97 /// # Returns
98 ///
99 /// Success after writing the text.
100 ///
101 /// # Errors
102 ///
103 /// Propagates a destination formatting error.
104 #[inline(always)]
105 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
106 formatter.write_str(self.as_str())
107 }
108}