wasmer_deploy_schema/schema/entity/
kind.rs1use std::borrow::Cow;
2
3#[derive(PartialEq, Eq, Clone, Debug)]
8pub struct EntityKind {
9 value: Cow<'static, str>,
10 name_start: usize,
11 version_start: usize,
12}
13
14impl EntityKind {
15 pub fn parse_parts(s: &str) -> Result<(usize, usize), EntityKindParseError> {
16 const fn is_valid_kind_char(c: char) -> bool {
17 match c {
18 'a'..='z' | 'A'..='Z' | '0'..='9' => true,
19 _ => false,
20 }
21 }
22
23 let (rest, version) = match s.rsplit_once(".v") {
24 Some(x) => x,
25 None => return Err(EntityKindParseError::MissingVersion),
26 };
27
28 let (ns, name) = match rest.rsplit_once('/') {
29 Some(x) => x,
30 None => return Err(EntityKindParseError::MissingNamespace),
31 };
32
33 if ns.is_empty() {
34 return Err(EntityKindParseError::MissingNamespace);
35 }
36 if let Some(c) = ns.chars().find(|c| !is_valid_kind_char(*c)) {
37 return Err(EntityKindParseError::InvalidNamespaceCharacter(c));
38 }
39
40 if name.is_empty() {
41 return Err(EntityKindParseError::MissingName);
42 }
43 if let Some(c) = name.chars().find(|c| !is_valid_kind_char(*c)) {
44 return Err(EntityKindParseError::InvalidNameCharacter(c));
45 }
46
47 let len = s.len();
48 let version_start = len - version.len();
49 let name_start = version_start - 2 - name.len();
50
51 Ok((name_start, version_start))
52 }
53
54 pub fn parse(s: impl Into<String>) -> Result<Self, EntityKindParseError> {
55 let s = s.into();
56 let (name_start, version_start) = Self::parse_parts(&s)?;
57
58 Ok(Self {
59 value: Cow::Owned(s),
60 name_start,
61 version_start,
62 })
63 }
64
65 pub fn namespace(&self) -> &str {
81 &self.value[..self.name_start - 1]
82 }
83
84 pub fn name(&self) -> &str {
85 &self.value[self.name_start..self.version_start - 2]
86 }
87
88 pub fn version(&self) -> &str {
89 &self.value[self.version_start..]
90 }
91}
92
93impl serde::Serialize for EntityKind {
94 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
95 serializer.serialize_str(&self.value)
96 }
97}
98
99impl<'de> serde::Deserialize<'de> for EntityKind {
100 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
101 let s = String::deserialize(deserializer)?;
102 EntityKind::parse(s).map_err(serde::de::Error::custom)
103 }
104}
105
106impl schemars::JsonSchema for EntityKind {
107 fn schema_name() -> String {
108 "EntityKind".to_string()
109 }
110
111 fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
112 schemars::schema::Schema::Object(schemars::schema::SchemaObject {
113 instance_type: Some(schemars::schema::InstanceType::String.into()),
114 ..Default::default()
115 })
116 }
117}
118
119impl std::str::FromStr for EntityKind {
120 type Err = EntityKindParseError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 EntityKind::parse(s)
124 }
125}
126
127#[derive(Clone, Debug)]
129pub enum EntityKindParseError {
130 MissingVersion,
131 MissingNamespace,
132 MissingName,
133 InvalidNamespaceCharacter(char),
134 InvalidNameCharacter(char),
135 InvalidVersion,
136}
137
138impl std::fmt::Display for EntityKindParseError {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 EntityKindParseError::MissingVersion => write!(f, "missing version"),
142 EntityKindParseError::MissingNamespace => write!(f, "missing namespace"),
143 EntityKindParseError::MissingName => write!(f, "missing name"),
144 EntityKindParseError::InvalidNamespaceCharacter(c) => {
145 write!(f, "invalid character in namespace: '{c}'")
146 }
147 EntityKindParseError::InvalidNameCharacter(c) => {
148 write!(f, "invalid character in name: '{}'", c)
149 }
150 EntityKindParseError::InvalidVersion => write!(f, "invalid version"),
151 }
152 }
153}
154
155impl std::error::Error for EntityKindParseError {}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn parse_entity_kind() {
163 let e = EntityKind::parse("a/b.v1").unwrap();
164 assert_eq!(e.namespace(), "a");
165 assert_eq!(e.name(), "b");
166 assert_eq!(e.version(), "1");
167 }
168}