Skip to main content

qubit_redact/http/
http_redaction_policy.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//! Immutable policy snapshot for every HTTP redaction context.
9
10use crate::{
11    DiagnosticBudget,
12    RedactionPolicy,
13};
14
15use super::{
16    BodyBudget,
17    HttpRedactionPolicyBuilder,
18    TextBodyPolicy,
19    UnkeyedJsonValuePolicy,
20    UrlPathPolicy,
21};
22
23/// Combines independent HTTP field policies, behavior choices, and hard limits.
24#[must_use]
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct HttpRedactionPolicy {
27    /// Field policy used for HTTP header names and values.
28    header_policy: RedactionPolicy,
29    /// Field policy used for URL query and form names and values.
30    query_policy: RedactionPolicy,
31    /// Field policy used inside structured HTTP body formats.
32    body_policy: RedactionPolicy,
33    /// Visibility choice for non-root URL paths.
34    url_path_policy: UrlPathPolicy,
35    /// Visibility choice for opaque UTF-8 text bodies.
36    text_body_policy: TextBodyPolicy,
37    /// Visibility choice for JSON scalar values without a field name.
38    unkeyed_json_value_policy: UnkeyedJsonValuePolicy,
39    /// Finite parser-input and log-output byte limits.
40    body_budget: BodyBudget,
41    /// Finite input and output limits for non-body diagnostics.
42    diagnostic_budget: DiagnosticBudget,
43}
44
45impl HttpRedactionPolicy {
46    /// Creates a builder without header, query, or body field rules.
47    ///
48    /// # Returns
49    ///
50    /// A mutable HTTP policy builder with fail-closed behavior defaults and
51    /// finite budgets.
52    #[inline(always)]
53    pub fn builder() -> HttpRedactionPolicyBuilder {
54        HttpRedactionPolicyBuilder::new()
55    }
56
57    /// Creates a builder initialized from the current default HTTP policy.
58    ///
59    /// # Returns
60    ///
61    /// A mutable HTTP policy builder containing a snapshot of the current
62    /// default policy.
63    #[inline(always)]
64    pub fn builder_from_default() -> HttpRedactionPolicyBuilder {
65        HttpRedactionPolicyBuilder::from_policy(&Self::default())
66    }
67
68    /// Creates a builder with three mutable copies of `base`.
69    ///
70    /// # Parameters
71    ///
72    /// * `base` - Field policy copied for header, query, and body contexts.
73    ///
74    /// # Returns
75    ///
76    /// A mutable HTTP policy builder using fail-closed behavior defaults and
77    /// `base`'s diagnostic budget snapshot.
78    pub fn builder_from(base: RedactionPolicy) -> HttpRedactionPolicyBuilder {
79        HttpRedactionPolicyBuilder::from_base_policy(base)
80    }
81
82    /// Creates an immutable HTTP policy from complete builder state.
83    ///
84    /// # Parameters
85    ///
86    /// * `header_policy` - Header field-policy snapshot.
87    /// * `query_policy` - Query and form field-policy snapshot.
88    /// * `body_policy` - Structured-body field-policy snapshot.
89    /// * `url_path_policy` - URL path visibility choice.
90    /// * `text_body_policy` - Opaque text-body visibility choice.
91    /// * `unkeyed_json_value_policy` - Unkeyed scalar visibility choice.
92    /// * `body_budget` - Checked body input and output byte limits.
93    ///
94    /// # Returns
95    ///
96    /// A complete immutable HTTP policy.
97    #[inline(always)]
98    pub(super) fn from_parts(
99        header_policy: RedactionPolicy,
100        query_policy: RedactionPolicy,
101        body_policy: RedactionPolicy,
102        url_path_policy: UrlPathPolicy,
103        text_body_policy: TextBodyPolicy,
104        unkeyed_json_value_policy: UnkeyedJsonValuePolicy,
105        body_budget: BodyBudget,
106    ) -> Self {
107        let diagnostic_budget = body_policy.diagnostic_budget();
108        Self {
109            header_policy,
110            query_policy,
111            body_policy,
112            url_path_policy,
113            text_body_policy,
114            unkeyed_json_value_policy,
115            body_budget,
116            diagnostic_budget,
117        }
118    }
119
120    /// Replaces the non-body diagnostic input and output byte limits.
121    ///
122    /// # Parameters
123    ///
124    /// * `diagnostic_budget` - Replacement limits for non-body diagnostics.
125    ///
126    /// # Returns
127    ///
128    /// The policy with its diagnostic budget replaced.
129    #[inline(always)]
130    pub(super) const fn with_diagnostic_budget(
131        mut self,
132        diagnostic_budget: DiagnosticBudget,
133    ) -> Self {
134        self.diagnostic_budget = diagnostic_budget;
135        self
136    }
137
138    /// Returns the immutable header-field policy snapshot.
139    ///
140    /// # Returns
141    ///
142    /// The policy used for HTTP headers.
143    #[inline(always)]
144    pub const fn header_policy(&self) -> &RedactionPolicy {
145        &self.header_policy
146    }
147
148    /// Returns the immutable query and form field-policy snapshot.
149    ///
150    /// # Returns
151    ///
152    /// The policy used for URL query and form fields.
153    #[inline(always)]
154    pub const fn query_policy(&self) -> &RedactionPolicy {
155        &self.query_policy
156    }
157
158    /// Returns the immutable structured-body field-policy snapshot.
159    ///
160    /// # Returns
161    ///
162    /// The policy used for fields inside HTTP bodies.
163    #[inline(always)]
164    pub const fn body_policy(&self) -> &RedactionPolicy {
165        &self.body_policy
166    }
167
168    /// Returns the URL path visibility choice.
169    ///
170    /// # Returns
171    ///
172    /// The immutable URL path behavior.
173    #[inline(always)]
174    pub const fn url_path_policy(&self) -> UrlPathPolicy {
175        self.url_path_policy
176    }
177
178    /// Returns the opaque text-body visibility choice.
179    ///
180    /// # Returns
181    ///
182    /// The immutable text-body behavior.
183    #[inline(always)]
184    pub const fn text_body_policy(&self) -> TextBodyPolicy {
185        self.text_body_policy
186    }
187
188    /// Returns the unkeyed JSON scalar visibility choice.
189    ///
190    /// # Returns
191    ///
192    /// The immutable unkeyed JSON behavior.
193    #[inline(always)]
194    pub const fn unkeyed_json_value_policy(&self) -> UnkeyedJsonValuePolicy {
195        self.unkeyed_json_value_policy
196    }
197
198    /// Returns the finite body input and output limits.
199    ///
200    /// # Returns
201    ///
202    /// The checked hard body budget.
203    #[inline(always)]
204    pub const fn body_budget(&self) -> BodyBudget {
205        self.body_budget
206    }
207
208    /// Returns the finite diagnostic input and output limits.
209    ///
210    /// # Returns
211    ///
212    /// The checked hard diagnostic budget.
213    #[inline(always)]
214    pub const fn diagnostic_budget(&self) -> DiagnosticBudget {
215        self.diagnostic_budget
216    }
217}
218
219impl Default for HttpRedactionPolicy {
220    /// Creates a fail-closed HTTP policy from the current global field default.
221    ///
222    /// # Returns
223    ///
224    /// Three independent default policy snapshots and finite body limits.
225    #[inline(always)]
226    fn default() -> Self {
227        let base = RedactionPolicy::default();
228        Self::from_parts(
229            base.clone(),
230            base.clone(),
231            base,
232            UrlPathPolicy::default(),
233            TextBodyPolicy::default(),
234            UnkeyedJsonValuePolicy::default(),
235            BodyBudget::default(),
236        )
237    }
238}