Skip to main content

qubit_redact/policy/redaction_policy_builder/
http_context_builder_view.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//! Transactional view over one HTTP field context.
9
10use super::PolicyError;
11use super::RedactionFloor;
12use super::RedactionRules;
13use super::Sensitivity;
14
15/// Mutable view over one HTTP field context.
16///
17/// # Examples
18///
19/// ```
20/// use qubit_redact::RedactionPolicy;
21/// use qubit_redact::Sensitivity;
22///
23/// let policy = RedactionPolicy::builder().http(|http| {
24///     http.header().raise("x-pin", Sensitivity::Secret).expect("valid header rule");
25/// })?.build()?;
26/// assert!(!policy.is_disabled());
27/// # Ok::<(), qubit_redact::PolicyError>(())
28/// ```
29#[cfg(feature = "http")]
30pub struct HttpContextBuilderView<'a> {
31    /// HTTP builder receiving context-specific changes.
32    pub(super) builder: &'a mut crate::formats::http::HttpPolicyBuilder,
33    /// Shared transaction error slot.
34    pub(super) error: &'a mut Option<PolicyError>,
35    /// Field context targeted by this view.
36    pub(super) context: crate::formats::http::HttpFieldContext,
37}
38
39#[cfg(feature = "http")]
40impl HttpContextBuilderView<'_> {
41    /// Replaces all rules for this HTTP field context.
42    #[inline(always)]
43    pub fn replace_rules(&mut self, rules: RedactionRules) -> &mut Self {
44        self.builder.rules_mut(self.context, rules);
45        self
46    }
47
48    /// Raises a context field's minimum sensitivity.
49    ///
50    /// # Errors
51    ///
52    /// Returns [`PolicyError`] when `field` has no canonical name.
53    pub fn raise(&mut self, field: &str, level: Sensitivity) -> Result<&mut Self, PolicyError> {
54        if self.error.is_none()
55            && let Err(error) = self.builder.raise_mut(self.context, field, level)
56        {
57            *self.error = Some(error.clone());
58            return Err(error);
59        }
60        Ok(self)
61    }
62
63    /// Replaces a context field rule without weakening the base policy.
64    ///
65    /// # Errors
66    ///
67    /// Returns [`PolicyError`] when `field` has no canonical name.
68    pub fn override_level(&mut self, field: &str, level: Sensitivity) -> Result<&mut Self, PolicyError> {
69        if self.error.is_none()
70            && let Err(error) = self.builder.override_level_mut(self.context, field, level)
71        {
72            *self.error = Some(error.clone());
73            return Err(error);
74        }
75        Ok(self)
76    }
77
78    /// Adds a context exact allow rule; the base policy still applies.
79    ///
80    /// # Errors
81    ///
82    /// Returns [`PolicyError`] when `field` has no canonical name.
83    pub fn allow_exact(&mut self, field: &str) -> Result<&mut Self, PolicyError> {
84        if self.error.is_none()
85            && let Err(error) = self.builder.allow_exact_mut(self.context, field)
86        {
87            *self.error = Some(error.clone());
88            return Err(error);
89        }
90        Ok(self)
91    }
92
93    /// Adds a context suffix allow rule; the base policy still applies.
94    ///
95    /// # Errors
96    ///
97    /// Returns [`PolicyError`] when `field` has no canonical suffix.
98    pub fn allow_suffix(&mut self, field: &str) -> Result<&mut Self, PolicyError> {
99        if self.error.is_none()
100            && let Err(error) = self.builder.allow_suffix_mut(self.context, field)
101        {
102            *self.error = Some(error.clone());
103            return Err(error);
104        }
105        Ok(self)
106    }
107
108    /// Removes a context exact allow rule.
109    ///
110    /// # Errors
111    ///
112    /// Returns [`PolicyError`] when `field` has no canonical name.
113    pub fn remove_allow_exact(&mut self, field: &str) -> Result<&mut Self, PolicyError> {
114        if self.error.is_none()
115            && let Err(error) = self.builder.remove_allow_exact_mut(self.context, field)
116        {
117            *self.error = Some(error.clone());
118            return Err(error);
119        }
120        Ok(self)
121    }
122
123    /// Removes a context suffix allow rule.
124    ///
125    /// # Errors
126    ///
127    /// Returns [`PolicyError`] when `field` has no canonical suffix.
128    pub fn remove_allow_suffix(&mut self, field: &str) -> Result<&mut Self, PolicyError> {
129        if self.error.is_none()
130            && let Err(error) = self.builder.remove_allow_suffix_mut(self.context, field)
131        {
132            *self.error = Some(error.clone());
133            return Err(error);
134        }
135        Ok(self)
136    }
137
138    /// Removes all context allow rules.
139    #[inline(always)]
140    pub fn clear_allow_rules(&mut self) -> &mut Self {
141        self.builder.clear_allow_rules_mut(self.context);
142        self
143    }
144
145    /// Adds a context floor. Base protection remains independently
146    /// effective.
147    #[must_use]
148    #[inline(always)]
149    pub fn floor(&mut self, floor: RedactionFloor) -> &mut Self {
150        self.builder.floor_mut(self.context, floor);
151        self
152    }
153
154    /// Disables this context's explicit floor.
155    #[inline(always)]
156    pub fn disable_floor(&mut self) -> &mut Self {
157        self.builder.disable_floor_mut(self.context);
158        self
159    }
160}