kustos_shared/
resource.rs1use std::{fmt::Display, num::ParseIntError, ops::Deref, str::FromStr};
16
17use snafu::Snafu;
18
19#[derive(Debug, Snafu)]
23pub enum ResourceParseError {
24 #[snafu(display("Invalid UUID: {source}"), context(false))]
25 Uuid {
26 #[snafu(source(from(uuid::Error, Box::new)))]
27 source: Box<uuid::Error>,
28 },
29
30 #[snafu(display("Invalid integer: {source}"), context(false))]
31 ParseInt {
32 #[snafu(source(from(ParseIntError, Box::new)))]
33 source: Box<ParseIntError>,
34 },
35
36 #[snafu(whatever)]
37 Other {
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 Resource: Sized + Display + KustosFromStr {
48 const PREFIX: &'static str;
55
56 fn resource_id(&self) -> ResourceId {
58 debug_assert!(Self::PREFIX.starts_with('/') && Self::PREFIX.ends_with('/'));
60
61 ResourceId(format!("{}{}", Self::PREFIX, self))
62 }
63}
64
65pub trait KustosFromStr: Sized {
66 fn kustos_from_str(s: &str) -> Result<Self, ResourceParseError>;
67}
68
69impl<T: Resource + FromStr<Err = E>, E: Into<ResourceParseError>> KustosFromStr for T {
70 fn kustos_from_str(s: &str) -> Result<Self, ResourceParseError> {
71 Self::from_str(s).map_err(Into::into)
72 }
73}
74
75#[derive(Debug, Clone, Hash, PartialEq, Eq)]
83pub struct ResourceId(pub(crate) String);
84
85impl ResourceId {
86 pub fn into_inner(self) -> String {
87 self.0
88 }
89
90 pub fn with_suffix<S>(&self, suffix: S) -> ResourceId
91 where
92 S: AsRef<str>,
93 {
94 let mut inner = self.0.clone();
95 inner.push_str(suffix.as_ref());
96 ResourceId(inner)
97 }
98
99 pub fn as_str(&self) -> &str {
100 self.0.as_str()
101 }
102}
103
104impl Display for ResourceId {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(f, "{}", self.as_str())
107 }
108}
109
110impl AsRef<str> for ResourceId {
111 fn as_ref(&self) -> &str {
112 self.as_str()
113 }
114}
115
116impl Deref for ResourceId {
117 type Target = String;
118
119 fn deref(&self) -> &Self::Target {
120 &self.0
121 }
122}
123
124impl From<&str> for ResourceId {
125 fn from(s: &str) -> Self {
126 Self(s.to_string())
127 }
128}
129
130impl From<String> for ResourceId {
131 fn from(s: String) -> Self {
132 Self(s)
133 }
134}
135#[derive(Debug)]
140pub enum AccessibleResources<T: Resource> {
141 List(Vec<T>),
142 All,
143}
144
145#[cfg(test)]
146mod tests {
147 use pretty_assertions::assert_eq;
148
149 use super::*;
150
151 struct ResourceX(uuid::Uuid);
152
153 impl Display for ResourceX {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 self.0.as_hyphenated().fmt(f)
156 }
157 }
158
159 impl Resource for ResourceX {
160 const PREFIX: &'static str = "/resources/";
161 }
162
163 impl FromStr for ResourceX {
164 type Err = ResourceParseError;
165 fn from_str(s: &str) -> Result<Self, Self::Err> {
166 s.parse().map(Self).map_err(Into::into)
167 }
168 }
169
170 #[test]
171 fn test_a() {
172 let x = ResourceId("/resources/00000000-0000-0000-0000-000000000000".to_string());
173
174 let target: ResourceX = x.strip_prefix(ResourceX::PREFIX).unwrap().parse().unwrap();
175 assert_eq!(target.0, uuid::Uuid::nil())
176 }
177}