Skip to main content

qubit_sanitize/core/
sensitive_fields.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use std::collections::BTreeMap;
11
12use super::{
13    SensitiveFieldPreset,
14    SensitivityLevel,
15    canonicalize_field_name,
16    default_sensitive_fields::DEFAULT_EXTRA_FIELDS,
17};
18
19/// Set of sensitive field names and their sensitivity levels.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct SensitiveFields {
22    /// Canonical field names mapped to sensitivity levels.
23    fields: BTreeMap<String, SensitivityLevel>,
24}
25
26impl SensitiveFields {
27    /// Creates an empty sensitive field set.
28    ///
29    /// # Returns
30    ///
31    /// Empty field set without built-in names.
32    pub fn new() -> Self {
33        Self {
34            fields: BTreeMap::new(),
35        }
36    }
37
38    /// Inserts one sensitive field name.
39    ///
40    /// # Parameters
41    ///
42    /// * `field` - Field name to mark sensitive.
43    /// * `level` - Sensitivity level assigned to the field.
44    pub fn insert(&mut self, field: &str, level: SensitivityLevel) {
45        let field = canonicalize_field_name(field);
46        if !field.is_empty() {
47            self.fields.insert(field, level);
48        }
49    }
50
51    /// Inserts each field with the same sensitivity level.
52    ///
53    /// # Parameters
54    ///
55    /// * `fields` - Field names to add.
56    /// * `level` - Sensitivity level assigned to every field.
57    pub fn extend<I, S>(&mut self, fields: I, level: SensitivityLevel)
58    where
59        I: IntoIterator<Item = S>,
60        S: AsRef<str>,
61    {
62        for field in fields {
63            self.insert(field.as_ref(), level);
64        }
65    }
66
67    /// Extends this set with one predefined field group.
68    ///
69    /// # Parameters
70    ///
71    /// * `preset` - Predefined group to insert.
72    pub fn extend_preset(&mut self, preset: SensitiveFieldPreset) {
73        for (field, level) in preset.fields() {
74            self.insert(field, *level);
75        }
76    }
77
78    /// Returns whether a field is configured as sensitive.
79    ///
80    /// # Parameters
81    ///
82    /// * `field` - Field name to test.
83    ///
84    /// # Returns
85    ///
86    /// `true` when `field` has a configured sensitivity level.
87    pub fn contains(&self, field: &str) -> bool {
88        self.level_for(field).is_some()
89    }
90
91    /// Returns the sensitivity level for a field.
92    ///
93    /// # Parameters
94    ///
95    /// * `field` - Field name to resolve.
96    ///
97    /// # Returns
98    ///
99    /// `Some(level)` when the field is sensitive, otherwise `None`.
100    pub fn level_for(&self, field: &str) -> Option<SensitivityLevel> {
101        self.fields.get(&canonicalize_field_name(field)).copied()
102    }
103
104    /// Returns the number of configured sensitive fields.
105    ///
106    /// # Returns
107    ///
108    /// Field count.
109    pub fn len(&self) -> usize {
110        self.fields.len()
111    }
112
113    /// Returns whether no fields are configured.
114    ///
115    /// # Returns
116    ///
117    /// `true` when the set is empty.
118    pub fn is_empty(&self) -> bool {
119        self.fields.is_empty()
120    }
121
122    /// Iterates canonical field names and sensitivity levels.
123    ///
124    /// # Returns
125    ///
126    /// Iterator over canonical field names and their levels.
127    pub fn iter(&self) -> impl Iterator<Item = (&str, SensitivityLevel)> {
128        self.fields
129            .iter()
130            .map(|(field, level)| (field.as_str(), *level))
131    }
132}
133
134impl Default for SensitiveFields {
135    /// Creates a set containing built-in sensitive fields.
136    fn default() -> Self {
137        let mut fields = Self::new();
138        for preset in [
139            SensitiveFieldPreset::Credentials,
140            SensitiveFieldPreset::AuthTokens,
141            SensitiveFieldPreset::Http,
142            SensitiveFieldPreset::Session,
143        ] {
144            fields.extend_preset(preset);
145        }
146        for (field, level) in DEFAULT_EXTRA_FIELDS {
147            fields.insert(field, level);
148        }
149        fields
150    }
151}