Skip to main content

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}