1use std::fmt;
7
8#[derive(Debug)]
10pub enum PolicyError<R, P> {
11 Policy(P),
13 Repository(R)
15}
16
17impl<R, P> PolicyError<R, P> {
18 pub const fn is_policy(&self) -> bool {
20 matches!(self, Self::Policy(_))
21 }
22
23 pub const fn is_repository(&self) -> bool {
25 matches!(self, Self::Repository(_))
26 }
27}
28
29impl<R: fmt::Display, P: fmt::Display> fmt::Display for PolicyError<R, P> {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::Policy(e) => write!(f, "authorization denied: {e}"),
33 Self::Repository(e) => write!(f, "repository error: {e}")
34 }
35 }
36}
37
38impl<R: std::error::Error + 'static, P: std::error::Error + 'static> std::error::Error
39 for PolicyError<R, P>
40{
41 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42 match self {
43 Self::Policy(e) => Some(e),
44 Self::Repository(e) => Some(e)
45 }
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub enum PolicyOperation {
52 Create,
54 Read,
56 Update,
58 Delete,
60 List,
62 Command
64}
65
66impl PolicyOperation {
67 #[must_use]
69 pub const fn is_read_only(&self) -> bool {
70 matches!(self, Self::Read | Self::List)
71 }
72
73 #[must_use]
75 pub const fn is_mutation(&self) -> bool {
76 !self.is_read_only()
77 }
78}
79
80#[cfg(test)]
81#[allow(clippy::uninlined_format_args)]
82mod tests {
83 use std::error::Error;
84
85 use super::*;
86
87 #[derive(Debug)]
88 struct TestError(&'static str);
89
90 impl fmt::Display for TestError {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "{}", self.0)
93 }
94 }
95
96 impl std::error::Error for TestError {}
97
98 #[test]
99 fn policy_error_is_policy() {
100 let err: PolicyError<TestError, TestError> = PolicyError::Policy(TestError("denied"));
101 assert!(err.is_policy());
102 assert!(!err.is_repository());
103 }
104
105 #[test]
106 fn policy_error_is_repository() {
107 let err: PolicyError<TestError, TestError> = PolicyError::Repository(TestError("db"));
108 assert!(err.is_repository());
109 assert!(!err.is_policy());
110 }
111
112 #[test]
113 fn policy_error_display() {
114 let policy: PolicyError<TestError, TestError> = PolicyError::Policy(TestError("denied"));
115 assert_eq!(format!("{}", policy), "authorization denied: denied");
116
117 let repo: PolicyError<TestError, TestError> = PolicyError::Repository(TestError("db"));
118 assert_eq!(format!("{}", repo), "repository error: db");
119 }
120
121 #[test]
122 fn policy_error_source() {
123 let policy: PolicyError<TestError, TestError> = PolicyError::Policy(TestError("denied"));
124 assert!(policy.source().is_some());
125
126 let repo: PolicyError<TestError, TestError> = PolicyError::Repository(TestError("db"));
127 assert!(repo.source().is_some());
128 }
129
130 #[test]
131 fn policy_operation_is_read_only() {
132 assert!(PolicyOperation::Read.is_read_only());
133 assert!(PolicyOperation::List.is_read_only());
134 assert!(!PolicyOperation::Create.is_read_only());
135 assert!(!PolicyOperation::Update.is_read_only());
136 assert!(!PolicyOperation::Delete.is_read_only());
137 assert!(!PolicyOperation::Command.is_read_only());
138 }
139
140 #[test]
141 fn policy_operation_is_mutation() {
142 assert!(!PolicyOperation::Read.is_mutation());
143 assert!(!PolicyOperation::List.is_mutation());
144 assert!(PolicyOperation::Create.is_mutation());
145 assert!(PolicyOperation::Update.is_mutation());
146 assert!(PolicyOperation::Delete.is_mutation());
147 assert!(PolicyOperation::Command.is_mutation());
148 }
149}