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}