Skip to main content

qubit_redact/formats/argv/
argv_redaction_writer.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//! Shared-session argument-vector redaction.
9
10use super::ArgvItem;
11use super::redaction::redact_heuristically_with_policy;
12use super::redaction::redact_items_with_policy;
13use crate::runtime::TextSession;
14use crate::runtime::collect_flat_format_items;
15use crate::runtime::runtime_session::RuntimeSession;
16
17/// A borrowed argv façade over one mutable diagnostic session.
18///
19/// # Type Parameters
20///
21/// * `'session` - Borrow of the parent composer's unpublished transaction.
22///
23/// # Examples
24///
25/// ```
26/// use std::ffi::OsStr;
27/// use qubit_redact::{Redactor, Sensitivity};
28/// use qubit_redact::formats::argv::ArgvItem;
29///
30/// let output = Redactor::standard().text_composer().argv(|argv| {
31///     argv.items([ArgvItem::sensitive(OsStr::new("raw-token"), Sensitivity::Secret)]);
32/// }).finish();
33/// assert!(!output.text().as_str().contains("raw-token"));
34/// ```
35pub struct ArgvRedactionWriter<'session> {
36    /// Shared policy and accounting owned by the parent session.
37    session: &'session mut TextSession,
38}
39
40impl<'session> ArgvRedactionWriter<'session> {
41    /// Creates a façade from a mutable diagnostic session.
42    ///
43    /// # Parameters
44    ///
45    /// * `session` - Parent transaction receiving all admitted arguments.
46    ///
47    /// # Returns
48    ///
49    /// A writer borrowing the transaction's existing policy and budget.
50    #[inline(always)]
51    #[must_use]
52    pub(crate) const fn new(session: &'session mut TextSession) -> Self {
53        Self { session }
54    }
55
56    /// Redacts items into the parent session's aggregate output.
57    ///
58    /// Plain items retain their values; use [`Self::heuristic_items`] to infer
59    /// sensitivity from supported option syntax. Iterator advancement stops
60    /// when shared structural admission fails or output is already closed.
61    ///
62    /// # Type Parameters
63    ///
64    /// * `'items` - Lifetime of borrowed argument values.
65    /// * `I` - Finite source of explicitly classified argument items.
66    ///
67    /// # Parameters
68    ///
69    /// * `items` - Arguments in their original command-line order.
70    ///
71    /// # Returns
72    ///
73    /// This writer for further operations in the same transaction.
74    pub fn items<'items, I>(&mut self, items: I) -> &mut Self
75    where
76        I: IntoIterator<Item = ArgvItem<'items>>,
77    {
78        if self.session.skip_aggregate_for_exhausted_output() {
79            return self;
80        }
81        let Some(items) = collect_flat_format_items(self.session, items, |item| item.value().as_encoded_bytes().len())
82        else {
83            return self;
84        };
85        let result = redact_items_with_policy(self.session.policy(), items, self.session.remaining_output_bytes());
86        self.session.append_rendered_operation(result);
87        self
88    }
89
90    /// Redacts heuristic items into the parent session's aggregate output.
91    ///
92    /// Supported option syntax is interpreted only for plain items; explicit
93    /// sensitivity is authoritative. This operation does not parse shell code.
94    ///
95    /// # Type Parameters
96    ///
97    /// * `'items` - Lifetime of borrowed argument values.
98    /// * `I` - Finite source of arguments to classify and render.
99    ///
100    /// # Parameters
101    ///
102    /// * `items` - Arguments in their original command-line order.
103    ///
104    /// # Returns
105    ///
106    /// This writer for further operations in the same transaction.
107    pub fn heuristic_items<'items, I>(&mut self, items: I) -> &mut Self
108    where
109        I: IntoIterator<Item = ArgvItem<'items>>,
110    {
111        if self.session.skip_aggregate_for_exhausted_output() {
112            return self;
113        }
114        let Some(items) = collect_flat_format_items(self.session, items, |item| item.value().as_encoded_bytes().len())
115        else {
116            return self;
117        };
118        let result =
119            redact_heuristically_with_policy(self.session.policy(), items, self.session.remaining_output_bytes());
120        self.session.append_rendered_operation(result);
121        self
122    }
123}