1use fd_core::{PolicyDecisionId, PolicyRuleId};
4use serde::{Deserialize, Serialize};
5
6use crate::trace::DecisionTrace;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct PolicyDecision {
11 pub id: PolicyDecisionId,
13
14 pub kind: PolicyDecisionKind,
16
17 pub reason: String,
19
20 pub rule_id: Option<PolicyRuleId>,
22
23 #[serde(default)]
25 pub metadata: serde_json::Value,
26
27 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub trace: Option<DecisionTrace>,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37#[serde(rename_all = "snake_case")]
38pub enum PolicyDecisionKind {
39 Allow,
41
42 Deny,
44
45 RequiresApproval,
47
48 AllowWithWarning,
50}
51
52impl PolicyDecision {
53 pub fn allow(reason: impl Into<String>) -> Self {
54 Self {
55 id: PolicyDecisionId::new(),
56 kind: PolicyDecisionKind::Allow,
57 reason: reason.into(),
58 rule_id: None,
59 metadata: serde_json::Value::Null,
60 trace: None,
61 }
62 }
63
64 pub fn deny(reason: impl Into<String>) -> Self {
65 Self {
66 id: PolicyDecisionId::new(),
67 kind: PolicyDecisionKind::Deny,
68 reason: reason.into(),
69 rule_id: None,
70 metadata: serde_json::Value::Null,
71 trace: None,
72 }
73 }
74
75 pub fn requires_approval(reason: impl Into<String>) -> Self {
76 Self {
77 id: PolicyDecisionId::new(),
78 kind: PolicyDecisionKind::RequiresApproval,
79 reason: reason.into(),
80 rule_id: None,
81 metadata: serde_json::Value::Null,
82 trace: None,
83 }
84 }
85
86 pub fn with_rule(mut self, rule_id: PolicyRuleId) -> Self {
87 self.rule_id = Some(rule_id);
88 self
89 }
90
91 pub fn with_trace(mut self, trace: DecisionTrace) -> Self {
95 self.trace = Some(trace);
96 self
97 }
98
99 pub fn is_allowed(&self) -> bool {
100 matches!(
101 self.kind,
102 PolicyDecisionKind::Allow | PolicyDecisionKind::AllowWithWarning
103 )
104 }
105
106 pub fn is_denied(&self) -> bool {
107 matches!(self.kind, PolicyDecisionKind::Deny)
108 }
109
110 pub fn needs_approval(&self) -> bool {
111 matches!(self.kind, PolicyDecisionKind::RequiresApproval)
112 }
113}