surrealdb_core/iam/entities/resources/
level.rs1use revision::revisioned;
2use std::{
3 collections::{HashMap, HashSet},
4 str::FromStr,
5};
6
7use cedar_policy::{Entity, EntityTypeName, EntityUid, RestrictedExpression};
8use serde::{Deserialize, Serialize};
9
10#[revisioned(revision = 1)]
11#[derive(Clone, Default, Debug, Eq, PartialEq, PartialOrd, Deserialize, Serialize, Hash)]
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
13#[non_exhaustive]
14pub enum Level {
15 #[default]
16 No,
17 Root,
18 Namespace(String),
19 Database(String, String),
20 Record(String, String, String),
21}
22
23impl std::fmt::Display for Level {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Level::No => write!(f, "No"),
27 Level::Root => write!(f, "/"),
28 Level::Namespace(ns) => write!(f, "/ns:{ns}/"),
29 Level::Database(ns, db) => write!(f, "/ns:{ns}/db:{db}/"),
30 Level::Record(ns, db, id) => write!(f, "/ns:{ns}/db:{db}/id:{id}/"),
31 }
32 }
33}
34
35impl Level {
36 pub fn level_name(&self) -> &str {
37 match self {
38 Level::No => "No",
39 Level::Root => "Root",
40 Level::Namespace(_) => "Namespace",
41 Level::Database(_, _) => "Database",
42 Level::Record(_, _, _) => "Record",
43 }
44 }
45
46 pub fn ns(&self) -> Option<&str> {
47 match self {
48 Level::Namespace(ns) => Some(ns),
49 Level::Database(ns, _) => Some(ns),
50 Level::Record(ns, _, _) => Some(ns),
51 _ => None,
52 }
53 }
54
55 pub fn db(&self) -> Option<&str> {
56 match self {
57 Level::Database(_, db) => Some(db),
58 Level::Record(_, db, _) => Some(db),
59 _ => None,
60 }
61 }
62
63 pub fn id(&self) -> Option<&str> {
64 match self {
65 Level::Record(_, _, id) => Some(id),
66 _ => None,
67 }
68 }
69
70 fn parent(&self) -> Option<Level> {
71 match self {
72 Level::No => None,
73 Level::Root => None,
74 Level::Namespace(_) => Some(Level::Root),
75 Level::Database(ns, _) => Some(Level::Namespace(ns.to_owned())),
76 Level::Record(ns, db, _) => Some(Level::Database(ns.to_owned(), db.to_owned())),
77 }
78 }
79
80 pub fn cedar_attrs(&self) -> HashMap<String, RestrictedExpression> {
82 let mut attrs = HashMap::with_capacity(5);
83 attrs.insert("type".into(), RestrictedExpression::new_string(self.level_name().to_owned()));
84
85 if let Some(ns) = self.ns() {
86 attrs.insert("ns".into(), RestrictedExpression::new_string(ns.to_owned()));
87 }
88
89 if let Some(db) = self.db() {
90 attrs.insert("db".into(), RestrictedExpression::new_string(db.to_owned()));
91 }
92
93 if let Some(id) = self.id() {
94 attrs.insert("id".into(), RestrictedExpression::new_string(id.to_owned()));
95 }
96
97 attrs
98 }
99
100 pub fn cedar_parents(&self) -> HashSet<EntityUid> {
101 if let Some(parent) = self.parent() {
102 return HashSet::from([parent.into()]);
103 }
104 HashSet::with_capacity(0)
105 }
106
107 pub fn cedar_entities(&self) -> Vec<Entity> {
108 let mut entities = Vec::new();
109
110 entities.push(self.into());
111
112 let mut parent = self.parent();
114 while let Some(p) = parent {
115 parent = p.parent();
116 entities.push(p.into());
117 }
118
119 entities
120 }
121}
122
123impl From<()> for Level {
124 fn from(_: ()) -> Self {
125 Level::Root
126 }
127}
128
129impl From<(&str,)> for Level {
130 fn from((ns,): (&str,)) -> Self {
131 Level::Namespace(ns.to_owned())
132 }
133}
134
135impl From<(&str, &str)> for Level {
136 fn from((ns, db): (&str, &str)) -> Self {
137 Level::Database(ns.to_owned(), db.to_owned())
138 }
139}
140
141impl From<(&str, &str, &str)> for Level {
142 fn from((ns, db, id): (&str, &str, &str)) -> Self {
143 Level::Record(ns.to_owned(), db.to_owned(), id.to_owned())
144 }
145}
146
147impl From<(Option<&str>, Option<&str>, Option<&str>)> for Level {
148 fn from(val: (Option<&str>, Option<&str>, Option<&str>)) -> Self {
149 match val {
150 (None, None, None) => ().into(),
151 (Some(ns), None, None) => (ns,).into(),
152 (Some(ns), Some(db), None) => (ns, db).into(),
153 (Some(ns), Some(db), Some(id)) => (ns, db, id).into(),
154 _ => Level::No,
155 }
156 }
157}
158
159impl std::convert::From<Level> for EntityUid {
160 fn from(level: Level) -> Self {
161 EntityUid::from_type_name_and_id(
162 EntityTypeName::from_str("Level").unwrap(),
163 format!("{}", level).parse().unwrap(),
164 )
165 }
166}
167
168impl std::convert::From<&Level> for EntityUid {
169 fn from(level: &Level) -> Self {
170 level.to_owned().into()
171 }
172}
173
174impl std::convert::From<Level> for Entity {
175 fn from(level: Level) -> Self {
176 Entity::new(level.to_owned().into(), level.cedar_attrs(), level.cedar_parents())
177 }
178}
179
180impl std::convert::From<&Level> for Entity {
181 fn from(level: &Level) -> Self {
182 level.to_owned().into()
183 }
184}
185
186impl std::convert::From<Level> for RestrictedExpression {
187 fn from(level: Level) -> Self {
188 format!("{}", EntityUid::from(level)).parse().unwrap()
189 }
190}
191
192impl std::convert::From<&Level> for RestrictedExpression {
193 fn from(level: &Level) -> Self {
194 level.to_owned().into()
195 }
196}