wasmer_deploy_schema/schema/entity/
uri.rs1#[derive(PartialEq, Eq, Clone, Debug)]
13pub struct EntityUri {
14 value: String,
15
16 type_start: usize,
17 version_start: usize,
18 name_start: usize,
19}
20
21impl PartialOrd for EntityUri {
22 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
23 self.value.partial_cmp(&other.value)
24 }
25}
26
27impl Ord for EntityUri {
28 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
29 self.value.cmp(&other.value)
30 }
31}
32
33impl std::hash::Hash for EntityUri {
34 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
35 self.value.hash(state);
36 }
37}
38
39impl EntityUri {
40 pub fn as_str(&self) -> &str {
41 &self.value
42 }
43
44 #[allow(clippy::inherent_to_string_shadow_display)]
46 pub fn to_string(&self) -> String {
47 self.value.clone()
48 }
49
50 pub fn into_string(self) -> String {
51 self.value
52 }
53
54 pub fn kind(&self) -> &str {
55 &self.value[..self.name_start - 1]
56 }
57
58 pub fn namespace(&self) -> &str {
59 &self.value[..self.type_start - 1]
60 }
61
62 pub fn entity_type(&self) -> &str {
63 &self.value[self.type_start..self.version_start - 2]
64 }
65
66 pub fn version(&self) -> &str {
67 &self.value[self.version_start..self.name_start - 1]
68 }
69
70 pub fn name(&self) -> &str {
71 &self.value[self.name_start..]
72 }
73
74 pub fn new_kind_name(kind: &str, name: &str) -> Result<Self, EntityUriParseError> {
75 Self::parse(format!("{}:{}", kind, name))
77 }
78
79 pub fn parse(s: impl Into<String>) -> Result<Self, EntityUriParseError> {
80 let s = s.into();
81 let (kind, name) = s.split_once(':').ok_or_else(|| {
82 EntityUriParseError::new(s.clone(), EntityUriParseErrorKind::MissingKindNameSeparator)
83 })?;
84
85 let (ns, kind) = kind.split_once('/').ok_or_else(|| {
86 EntityUriParseError::new(
87 s.clone(),
88 EntityUriParseErrorKind::MissingKindNamespaceSeparator,
89 )
90 })?;
91
92 let (ty, version) = kind.split_once('.').ok_or_else(|| {
93 EntityUriParseError::new(s.clone(), EntityUriParseErrorKind::InvalidType)
94 })?;
95
96 if !is_valid_namespace(ns) {
97 return Err(EntityUriParseError::new(
98 s,
99 EntityUriParseErrorKind::InvalidNamespace,
100 ));
101 }
102 if !is_valid_type_name(ty) {
103 return Err(EntityUriParseError::new(
104 s,
105 EntityUriParseErrorKind::InvalidType,
106 ));
107 }
108 if !is_valid_version(version) {
109 return Err(EntityUriParseError::new(
110 s,
111 EntityUriParseErrorKind::InvalidTypeVersion,
112 ));
113 }
114 if !is_valid_name(name) {
115 return Err(EntityUriParseError::new(
116 s,
117 EntityUriParseErrorKind::InvalidName,
118 ));
119 }
120
121 let type_start = ns.len() + 1;
122 let version_start = type_start + ty.len() + 2;
123 let name_start = version_start + version.len();
124
125 Ok(Self {
126 value: s,
127 type_start,
128 version_start,
129 name_start,
130 })
131 }
132}
133
134impl std::fmt::Display for EntityUri {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 self.value.fmt(f)
137 }
138}
139
140fn is_valid_namespace(s: &str) -> bool {
141 s.split('.').all(|p| {
142 !p.is_empty()
143 && p.chars().all(|c| match c {
144 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' => true,
145 _ => false,
146 })
147 })
148}
149
150fn is_valid_type_name(s: &str) -> bool {
151 !s.is_empty()
152 && s.chars().all(|c| match c {
153 'a'..='z' | 'A'..='Z' | '0'..='9' => true,
154 _ => false,
155 })
156}
157
158fn is_valid_version(s: &str) -> bool {
159 !s.is_empty()
160 && s.chars().all(|c| match c {
161 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' => true,
162 _ => false,
163 })
164}
165
166fn is_valid_name(name: &str) -> bool {
167 !name.is_empty()
168 && name.chars().all(|c| match c {
169 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_' | '.' | '+' => true,
170 _ => false,
171 })
172}
173
174impl std::str::FromStr for EntityUri {
175 type Err = EntityUriParseError;
176
177 fn from_str(s: &str) -> Result<Self, Self::Err> {
178 Self::parse(s.to_string())
179 }
180}
181
182impl serde::Serialize for EntityUri {
183 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
184 serializer.serialize_str(&self.value)
185 }
186}
187
188impl<'de> serde::Deserialize<'de> for EntityUri {
189 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
190 let value = String::deserialize(deserializer)?;
191
192 Self::parse(value).map_err(serde::de::Error::custom)
193 }
194}
195
196impl schemars::JsonSchema for EntityUri {
197 fn schema_name() -> String {
198 "EntityUri".to_string()
199 }
200
201 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
202 schemars::schema::Schema::Object(schemars::schema::SchemaObject {
203 instance_type: Some(schemars::schema::InstanceType::String.into()),
204 ..Default::default()
205 })
206 }
207}
208
209#[derive(Clone, Debug)]
210pub struct EntityUriParseError {
211 value: String,
212 kind: EntityUriParseErrorKind,
213}
214
215impl EntityUriParseError {
216 pub fn new(value: impl Into<String>, kind: EntityUriParseErrorKind) -> Self {
217 Self {
218 value: value.into(),
219 kind,
220 }
221 }
222}
223
224impl std::fmt::Display for EntityUriParseError {
225 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(_f, "invalid entity URI: '{}' ({:?})", self.value, self.kind)
227 }
228}
229
230impl std::error::Error for EntityUriParseError {}
231
232#[derive(Clone, Debug)]
233pub enum EntityUriParseErrorKind {
234 MissingKindNameSeparator,
235 MissingKindNamespaceSeparator,
236 InvalidNamespace,
237 InvalidType,
238 InvalidName,
239 InvalidTypeVersion,
240}
241
242impl std::fmt::Display for EntityUriParseErrorKind {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 match self {
245 EntityUriParseErrorKind::MissingKindNameSeparator => {
246 write!(f, "missing kind/name separator")
247 }
248 EntityUriParseErrorKind::MissingKindNamespaceSeparator => {
249 write!(f, "missing kind/namespace separator")
250 }
251 EntityUriParseErrorKind::InvalidNamespace => write!(f, "invalid namespace"),
252 EntityUriParseErrorKind::InvalidType => write!(f, "invalid type"),
253 EntityUriParseErrorKind::InvalidName => write!(f, "invalid name"),
254 EntityUriParseErrorKind::InvalidTypeVersion => write!(f, "invalid type version"),
255 }
256 }
257}
258
259#[derive(
261 serde::Serialize, serde::Deserialize, schemars::JsonSchema, PartialEq, Eq, Clone, Debug,
262)]
263pub enum EntityOrRef<T> {
264 #[serde(rename = "ref")]
265 Ref(EntityUri),
266 #[serde(rename = "item")]
267 Item(T),
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_entity_uri_parse() {
276 let u = EntityUri::parse("my-ns.com/Ent.v1:lala123".to_string()).unwrap();
277 assert_eq!(u.namespace(), "my-ns.com");
278 assert_eq!(u.entity_type(), "Ent");
279 assert_eq!(u.version(), "1");
280 assert_eq!(u.name(), "lala123");
281 }
282}