1use std::{fmt::Display, str::FromStr};
6
7use snafu::Snafu;
8use uuid::Uuid;
9
10#[derive(Debug, Snafu)]
14pub enum ParsingError {
15 #[snafu(display("Invalid access method: `{method}`"))]
16 InvalidAccessMethod { method: String },
17
18 #[snafu(display("String was not a PolicyUser casbin string: `{user}`"))]
19 PolicyUser { user: String },
20
21 #[snafu(display("String was not a PolicyInvite casbin string: `{invite}`"))]
22 PolicyInvite { invite: String },
23
24 #[snafu(display("String was not a PolicyInternalGroup casbin string: `{group}"))]
25 PolicyInternalGroup { group: String },
26
27 #[snafu(display("String was not a PolicyOPGroup casbin string: `{group}`"))]
28 PolicyOPGroup { group: String },
29
30 #[snafu(display("Invalid UUID: {source}"), context(false))]
31 Uuid {
32 #[snafu(source(from(uuid::Error, Box::new)))]
33 source: Box<uuid::Error>,
34 },
35
36 #[snafu(display("Custom: {message}"), whatever)]
37 Custom {
38 message: String,
39
40 #[snafu(source(from(Box<dyn std::error::Error + Send + Sync>, Some)))]
41 source: Option<Box<dyn std::error::Error + Send + Sync>>,
42 },
43}
44
45pub trait IsSubject {}
50
51#[derive(Debug, PartialEq, Eq, Clone, Hash)]
55pub struct PolicyUser(pub(crate) uuid::Uuid);
56
57impl IsSubject for PolicyUser {}
58
59impl PolicyUser {
60 pub const fn nil() -> Self {
62 Self(Uuid::nil())
63 }
64
65 pub const fn from_u128(id: u128) -> Self {
67 Self(Uuid::from_u128(id))
68 }
69
70 pub fn generate() -> Self {
72 Self(Uuid::new_v4())
73 }
74}
75
76impl Display for PolicyUser {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 write!(f, "{}", self.0)
79 }
80}
81
82impl From<uuid::Uuid> for PolicyUser {
83 fn from(user: uuid::Uuid) -> Self {
84 PolicyUser(user)
85 }
86}
87
88impl FromStr for PolicyUser {
89 type Err = ParsingError;
90
91 fn from_str(s: &str) -> Result<Self, Self::Err> {
92 if s.starts_with("user::") {
93 Ok(PolicyUser(uuid::Uuid::from_str(
94 s.trim_start_matches("user::"),
95 )?))
96 } else {
97 PolicyUserSnafu { user: s.to_owned() }.fail()
98 }
99 }
100}
101
102impl AsRef<uuid::Uuid> for PolicyUser {
103 fn as_ref(&self) -> &uuid::Uuid {
104 &self.0
105 }
106}
107
108#[derive(Debug, PartialEq, Eq, Clone, Hash)]
112pub struct PolicyInvite(pub(crate) uuid::Uuid);
113
114impl IsSubject for PolicyInvite {}
115
116impl PolicyInvite {
117 pub const fn nil() -> Self {
119 Self(Uuid::nil())
120 }
121
122 pub const fn from_u128(id: u128) -> Self {
124 Self(Uuid::from_u128(id))
125 }
126
127 pub fn generate() -> Self {
129 Self(Uuid::new_v4())
130 }
131}
132
133impl From<uuid::Uuid> for PolicyInvite {
134 fn from(invite: uuid::Uuid) -> Self {
135 PolicyInvite(invite)
136 }
137}
138
139impl FromStr for PolicyInvite {
140 type Err = ParsingError;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 if s.starts_with("invite::") {
144 Ok(PolicyInvite(uuid::Uuid::from_str(
145 s.trim_start_matches("invite::"),
146 )?))
147 } else {
148 PolicyInviteSnafu {
149 invite: s.to_owned(),
150 }
151 .fail()
152 }
153 }
154}
155
156impl AsRef<uuid::Uuid> for PolicyInvite {
157 fn as_ref(&self) -> &uuid::Uuid {
158 &self.0
159 }
160}
161
162#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct PolicyRole(pub(crate) String);
165
166impl IsSubject for PolicyRole {}
167
168impl From<String> for PolicyRole {
169 fn from(group: String) -> Self {
170 PolicyRole(group)
171 }
172}
173
174impl From<&str> for PolicyRole {
175 fn from(group: &str) -> Self {
176 PolicyRole(group.to_string())
177 }
178}
179
180impl FromStr for PolicyRole {
181 type Err = ParsingError;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 if s.starts_with("role::") {
185 Ok(PolicyRole(s.trim_start_matches("role::").to_string()))
186 } else {
187 PolicyInternalGroupSnafu {
188 group: s.to_owned(),
189 }
190 .fail()
191 }
192 }
193}
194
195impl AsRef<str> for PolicyRole {
196 fn as_ref(&self) -> &str {
197 self.0.as_ref()
198 }
199}
200
201#[derive(Debug, Clone, PartialEq, Eq)]
203pub struct PolicyGroup(pub(crate) String);
204
205impl IsSubject for PolicyGroup {}
206
207impl From<String> for PolicyGroup {
208 fn from(group: String) -> Self {
209 PolicyGroup(group)
210 }
211}
212impl From<&str> for PolicyGroup {
213 fn from(group: &str) -> Self {
214 PolicyGroup(group.to_string())
215 }
216}
217
218impl FromStr for PolicyGroup {
219 type Err = ParsingError;
220
221 fn from_str(s: &str) -> Result<Self, Self::Err> {
222 if s.starts_with("group::") {
223 Ok(PolicyGroup(s.trim_start_matches("group::").to_string()))
224 } else {
225 PolicyOPGroupSnafu {
226 group: s.to_owned(),
227 }
228 .fail()
229 }
230 }
231}
232
233pub struct UserToRole(pub PolicyUser, pub PolicyRole);
235
236pub struct UserToGroup(pub PolicyUser, pub PolicyGroup);
238
239pub struct GroupToRole(pub PolicyGroup, pub PolicyRole);
241
242#[cfg(test)]
243mod tests {
244 use std::str::FromStr;
245
246 use crate::subject::{ParsingError, PolicyInvite, PolicyUser};
247
248 #[test]
249 fn test_policy_invite_invalid_uuid() {
250 let raw_invite = "invite::00000000-0000-0000c0000-000000000000";
251 let parsing_result = PolicyInvite::from_str(raw_invite);
252 assert!(
253 matches!(parsing_result, Err(ParsingError::Uuid { source: _ }),),
254 "Expected Uuid error, Got: {parsing_result:?}",
255 );
256 }
257
258 #[test]
259 fn test_policy_user_invalid_uuid() {
260 let raw_invite = "user::00000000-0000-0000c0000-000000000000";
261 let parsing_result = PolicyUser::from_str(raw_invite);
262 assert!(
263 matches!(parsing_result, Err(ParsingError::Uuid { source: _ }),),
264 "Expected Uuid error, Got: {parsing_result:?}",
265 );
266 }
267}