qubit_redact/domain/bounded_redacted_display.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//! Byte-bounded display adapter for an already-redacted view.
9
10use std::fmt::{
11 self,
12 Debug,
13 Display,
14 Formatter,
15 Write as _,
16};
17
18use crate::{
19 LogOutputLimit,
20 text::internal::BoundedLogEscapeWriter,
21};
22
23use super::internal::with_mask_byte_limit;
24
25/// A redacted display view whose log-safe output cannot exceed a byte limit.
26///
27/// The limit includes the complete `<truncated>` marker. Truncation preserves
28/// UTF-8 character boundaries and never splits a generated escape sequence.
29///
30/// # Type Parameters
31///
32/// * `D` - Already-redacted debug value rendered by this adapter.
33#[must_use = "format the bounded redacted view"]
34pub struct BoundedRedactedDisplay<D> {
35 /// Already-redacted debug view to render.
36 value: D,
37 /// Validated maximum output byte count.
38 limit: LogOutputLimit,
39}
40
41impl<D> BoundedRedactedDisplay<D> {
42 /// Creates a bounded display adapter around an already-redacted view.
43 ///
44 /// # Parameters
45 ///
46 /// * `value` - Redacted view whose compact debug representation is safe.
47 /// * `limit` - Validated maximum output byte count.
48 ///
49 /// # Returns
50 ///
51 /// A display-only bounded adapter.
52 #[inline(always)]
53 pub(crate) const fn new(value: D, limit: LogOutputLimit) -> Self {
54 Self { value, limit }
55 }
56}
57
58impl<D: Debug> Display for BoundedRedactedDisplay<D> {
59 /// Writes escaped redacted output within the configured byte budget.
60 ///
61 /// # Parameters
62 ///
63 /// * `formatter` - Destination formatting context.
64 ///
65 /// # Returns
66 ///
67 /// The formatter result for the bounded output.
68 ///
69 /// # Errors
70 ///
71 /// Returns [`fmt::Error`] when redacted formatting or the destination
72 /// rejects output.
73 #[inline(always)]
74 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
75 format_bounded(&self.value, self.limit, formatter)
76 }
77}
78
79/// Formats a type-erased debug value through one bounded implementation.
80///
81/// # Parameters
82///
83/// * `value` - Already-redacted debug view to format.
84/// * `limit` - Validated maximum output byte count.
85/// * `formatter` - Destination formatting context.
86///
87/// # Returns
88///
89/// The formatter result for the bounded output.
90///
91/// # Errors
92///
93/// Returns [`fmt::Error`] when redacted formatting or the destination rejects
94/// output.
95fn format_bounded(
96 value: &dyn Debug,
97 limit: LogOutputLimit,
98 formatter: &mut Formatter<'_>,
99) -> fmt::Result {
100 let mut writer = BoundedLogEscapeWriter::new(limit);
101 let result = with_mask_byte_limit(limit.max_bytes(), || {
102 write!(&mut writer, "{value:?}")
103 });
104 if result.is_err() && !writer.is_truncated() {
105 return Err(fmt::Error);
106 }
107 formatter.write_str(&writer.finish())
108}