1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// =============================================================================
// Copyright (c) 2025 - 2026 Haixing Hu.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Explainable borrowed results from field-policy classification.
use super::{
AllowRule,
FieldMatchKind,
SensitiveFieldRule,
Sensitivity,
};
/// Explains why a field is sensitive, allowed, or unknown to a policy.
///
/// Matched rules borrow canonical field names from the immutable policy, so
/// callers can inspect classification without cloning rule metadata.
///
/// # Type Parameters
///
/// * `'a` - Lifetime of canonical field names borrowed from the policy.
#[must_use]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldClassification<'a> {
/// A sensitive rule classified the field.
Sensitive {
/// Borrowed configured rule that supplied the sensitivity.
rule: SensitiveFieldRule<'a>,
/// The canonical input candidate that matched this rule.
match_kind: FieldMatchKind,
},
/// An allow rule took precedence over sensitivity at the same candidate.
Allowed {
/// Borrowed configured rule that allowed the field.
rule: AllowRule<'a>,
/// The canonical input candidate that matched this rule.
match_kind: FieldMatchKind,
},
/// No configured rule classified the field.
Unknown,
}
impl<'a> FieldClassification<'a> {
/// Returns the configured sensitivity when the field is sensitive.
///
/// # Returns
///
/// `Some(level)` for [`Self::Sensitive`], or `None` for allowed and unknown
/// fields.
#[must_use]
pub const fn sensitivity(self) -> Option<Sensitivity> {
match self {
Self::Sensitive { rule, .. } => Some(rule.sensitivity()),
Self::Allowed { .. } | Self::Unknown => None,
}
}
/// Returns the canonical configured field that matched.
///
/// # Returns
///
/// A field name borrowed from the policy for sensitive and allowed
/// classifications, or `None` for [`Self::Unknown`].
#[must_use]
pub const fn matched_field(self) -> Option<&'a str> {
match self {
Self::Sensitive { rule, .. } => Some(rule.field()),
Self::Allowed { rule, .. } => Some(rule.field()),
Self::Unknown => None,
}
}
/// Returns the canonical input candidate that matched the configured rule.
///
/// # Returns
///
/// The exact input or a semantic token suffix for sensitive and allowed
/// classifications, or `None` for [`Self::Unknown`].
#[must_use]
pub const fn match_kind(self) -> Option<FieldMatchKind> {
match self {
Self::Sensitive { match_kind, .. }
| Self::Allowed { match_kind, .. } => Some(match_kind),
Self::Unknown => None,
}
}
/// Reports whether an allow rule classified the field.
///
/// # Returns
///
/// `true` only for [`Self::Allowed`].
#[must_use]
pub const fn is_allowed(self) -> bool {
matches!(self, Self::Allowed { .. })
}
/// Reports whether no configured rule classified the field.
///
/// # Returns
///
/// `true` only for [`Self::Unknown`].
#[must_use]
pub const fn is_unknown(self) -> bool {
matches!(self, Self::Unknown)
}
}