Skip to main content

qubit_redact/formats/process/
process_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//! Aggregate process-command rendering through one borrowed transaction.
9
10use std::ffi::OsStr;
11
12use super::command_items::CommandItems;
13use crate::formats::argv::ArgvItem;
14use crate::formats::argv::ArgvRedactionWriter;
15use crate::formats::env::EnvRedactionWriter;
16use crate::runtime::TextSession;
17use crate::runtime::runtime_session::RuntimeSession;
18
19/// A borrowed process-command facade over one active redaction transaction.
20///
21/// This type owns no redactor, policy, result, or budget. Each operation
22/// delegates directly to the argv or environment namespace of the parent
23/// session, so process diagnostics participate in the same atomic output.
24///
25/// # Type Parameters
26///
27/// * `'session` - Borrow of the parent composer's unpublished transaction.
28///
29/// # Examples
30///
31/// ```
32/// use std::ffi::OsStr;
33/// use qubit_redact::Redactor;
34/// use qubit_redact::formats::argv::ArgvItem;
35///
36/// let output = Redactor::standard().text_composer().process(|process| {
37///     process.command(
38///         OsStr::new("client"),
39///         [ArgvItem::plain(OsStr::new("--help"))],
40///         [(OsStr::new("PASSWORD"), OsStr::new("raw-password"))],
41///     );
42/// }).finish();
43/// assert!(!output.text().as_str().contains("raw-password"));
44/// ```
45pub struct ProcessRedactionWriter<'session> {
46    /// The transaction receiving every rendered process component.
47    session: &'session mut TextSession,
48}
49
50impl<'session> ProcessRedactionWriter<'session> {
51    /// Creates a process facade that borrows `session` for one adapter closure.
52    ///
53    /// # Parameters
54    ///
55    /// * `session` - The active transaction that owns policy, budget, and
56    ///   unpublished output.
57    ///
58    /// # Returns
59    ///
60    /// A façade that cannot outlive the borrowed transaction.
61    #[inline(always)]
62    #[must_use]
63    pub(crate) const fn new(session: &'session mut TextSession) -> Self {
64        Self { session }
65    }
66
67    /// Redacts a complete process command into the parent aggregate output.
68    ///
69    /// `program` is inserted as the first argv item. `arguments` therefore
70    /// must contain only the arguments following the executable. Each argument
71    /// preserves caller-supplied [`ArgvItem`] sensitivity. `variables` uses
72    /// borrowed operating-system name/value pairs and is rendered after argv.
73    /// No key or independently published result is created.
74    ///
75    /// # Type Parameters
76    ///
77    /// * `'arguments` - Lifetime of borrowed argument values.
78    /// * `A` - Finite argument source.
79    /// * `'variables` - Lifetime of borrowed environment names and values.
80    /// * `E` - Finite environment source.
81    ///
82    /// # Parameters
83    ///
84    /// * `program` - Executable path or program name.
85    /// * `arguments` - Arguments after `program`.
86    /// * `variables` - Environment-variable pairs associated with the command.
87    ///
88    /// # Returns
89    ///
90    /// This facade for further aggregate process operations.
91    pub fn command<'arguments, 'variables, A, E>(
92        &mut self,
93        program: &'arguments OsStr,
94        arguments: A,
95        variables: E,
96    ) -> &mut Self
97    where
98        A: IntoIterator<Item = ArgvItem<'arguments>>,
99        E: IntoIterator<Item = (&'variables OsStr, &'variables OsStr)>,
100    {
101        if self.session.skip_aggregate_for_exhausted_output() {
102            return self;
103        }
104        let arguments = arguments.into_iter();
105        let mut argv = ArgvRedactionWriter::new(self.session);
106        argv.heuristic_items(CommandItems::new(ArgvItem::plain(program), arguments));
107        if self.session.skip_aggregate_for_exhausted_output() {
108            return self;
109        }
110        let mut environment = EnvRedactionWriter::new(self.session);
111        environment.os_pairs(variables);
112        self
113    }
114
115    /// Redacts command-line arguments into the parent aggregate output.
116    ///
117    /// Plain arguments use the argv namespace's documented heuristic rules;
118    /// explicitly sensitive [`ArgvItem`] values are masked at their supplied
119    /// sensitivity. No key or standalone output is created.
120    ///
121    /// # Type Parameters
122    ///
123    /// * `'arguments` - Lifetime of borrowed argument values.
124    /// * `A` - Finite argument source.
125    ///
126    /// # Parameters
127    ///
128    /// * `arguments` - Borrowed argv items to render.
129    ///
130    /// # Returns
131    ///
132    /// This facade for further aggregate process operations.
133    #[inline(always)]
134    pub fn arguments<'arguments, A>(&mut self, arguments: A) -> &mut Self
135    where
136        A: IntoIterator<Item = ArgvItem<'arguments>>,
137    {
138        let mut argv = ArgvRedactionWriter::new(self.session);
139        argv.heuristic_items(arguments);
140        self
141    }
142
143    /// Redacts process environment variables into the parent aggregate output.
144    ///
145    /// Names and values remain borrowed until the environment namespace has
146    /// processed them. No key or standalone output is created.
147    ///
148    /// # Type Parameters
149    ///
150    /// * `'variables` - Lifetime of borrowed environment names and values.
151    /// * `E` - Finite environment source.
152    ///
153    /// # Parameters
154    ///
155    /// * `variables` - Borrowed operating-system environment name/value pairs.
156    ///
157    /// # Returns
158    ///
159    /// This facade for further aggregate process operations.
160    #[inline(always)]
161    pub fn variables<'variables, E>(&mut self, variables: E) -> &mut Self
162    where
163        E: IntoIterator<Item = (&'variables OsStr, &'variables OsStr)>,
164    {
165        let mut env = EnvRedactionWriter::new(self.session);
166        env.os_pairs(variables);
167        self
168    }
169}