kellnr_common/
original_name.rs1use std::convert::TryFrom;
2use std::fmt;
3use std::ops::Deref;
4use std::sync::LazyLock;
5
6use regex::Regex;
7use sea_orm::Value;
8use thiserror::Error;
9use utoipa::ToSchema;
10
11use crate::canonical_name::CanonicalName;
12
13static NAME_REGEX: LazyLock<Regex> =
14 LazyLock::new(|| Regex::new(r"^[a-zA-Z][a-zA-Z0-9-_]*$").unwrap());
15
16#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, Hash, ToSchema)]
17#[schema(value_type = String)]
18#[serde(try_from = "String")]
23pub struct OriginalName(String);
24
25#[derive(Debug, PartialEq, Eq, Error)]
26pub enum NameError {
27 #[error("Invalid character in name")]
28 InvalidCharacter,
29 #[error("Invalid length for name")]
30 InvalidLength,
31}
32
33impl OriginalName {
34 pub fn to_canonical(&self) -> CanonicalName {
35 CanonicalName::from(self)
36 }
37 pub fn from_unchecked(name: String) -> Self {
38 Self(name)
39 }
40 pub fn into_inner(self) -> String {
41 self.0
42 }
43}
44
45impl TryFrom<String> for OriginalName {
46 type Error = NameError;
47
48 fn try_from(package_name: String) -> Result<Self, Self::Error> {
49 if !NAME_REGEX.is_match(&package_name) {
50 Err(NameError::InvalidCharacter)
51 } else if package_name.len() > 64 {
52 Err(NameError::InvalidLength)
53 } else {
54 Ok(OriginalName(package_name))
55 }
56 }
57}
58
59impl From<&OriginalName> for String {
60 fn from(name: &OriginalName) -> Self {
61 name.to_string()
62 }
63}
64
65impl From<OriginalName> for String {
66 fn from(name: OriginalName) -> Self {
67 name.to_string()
68 }
69}
70
71impl From<OriginalName> for Value {
72 fn from(value: OriginalName) -> Self {
73 Value::String(Some(value.0))
74 }
75}
76
77impl From<&OriginalName> for Value {
78 fn from(value: &OriginalName) -> Self {
79 Value::String(Some(value.0.clone()))
80 }
81}
82
83impl TryFrom<&String> for OriginalName {
84 type Error = NameError;
85
86 fn try_from(package_name: &String) -> Result<Self, Self::Error> {
87 OriginalName::try_from(package_name.clone())
88 }
89}
90
91impl TryFrom<&str> for OriginalName {
92 type Error = NameError;
93
94 fn try_from(package_name: &str) -> Result<Self, Self::Error> {
95 OriginalName::try_from(package_name.to_string())
96 }
97}
98
99impl Deref for OriginalName {
100 type Target = String;
101
102 fn deref(&self) -> &Self::Target {
103 &self.0
104 }
105}
106
107impl fmt::Display for OriginalName {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 write!(f, "{}", self.0)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn valid_package_names() {
119 assert_eq!(
120 OriginalName::try_from("test-lib").unwrap(),
121 OriginalName("test-lib".to_string())
122 );
123 assert_eq!(
124 OriginalName::try_from("Test_lib").unwrap(),
125 OriginalName("Test_lib".to_string())
126 );
127 assert_eq!(
128 OriginalName::try_from("test_lib_foo").unwrap(),
129 OriginalName("test_lib_foo".to_string())
130 );
131 assert_eq!(
132 OriginalName::try_from("test-lIB-foo").unwrap(),
133 OriginalName("test-lIB-foo".to_string())
134 );
135 assert_eq!(
136 OriginalName::try_from("test12_14f").unwrap(),
137 OriginalName("test12_14f".to_string())
138 );
139 assert_eq!(
140 OriginalName::try_from("tEs2-23_1f").unwrap(),
141 OriginalName("tEs2-23_1f".to_string())
142 );
143 assert_eq!(
144 OriginalName::try_from("testlib").unwrap(),
145 OriginalName("testlib".to_string())
146 );
147 assert_eq!(
148 OriginalName::try_from("Testlib23").unwrap(),
149 OriginalName("Testlib23".to_string())
150 );
151 }
152
153 #[test]
154 fn invalid_characters_in_package_name() {
155 assert_eq!(
156 OriginalName::try_from("_test").unwrap_err(),
157 NameError::InvalidCharacter
158 );
159 assert_eq!(
160 OriginalName::try_from("44Test").unwrap_err(),
161 NameError::InvalidCharacter
162 );
163 assert_eq!(
164 OriginalName::try_from("-Av").unwrap_err(),
165 NameError::InvalidCharacter
166 );
167 assert_eq!(
168 OriginalName::try_from("testÄ").unwrap_err(),
169 NameError::InvalidCharacter
170 );
171 assert_eq!(
172 OriginalName::try_from("test?").unwrap_err(),
173 NameError::InvalidCharacter
174 );
175 assert_eq!(
176 OriginalName::try_from(".45A").unwrap_err(),
177 NameError::InvalidCharacter
178 );
179 assert_eq!(
180 OriginalName::try_from("").unwrap_err(),
181 NameError::InvalidCharacter
182 );
183 }
184
185 #[test]
186 fn deserialize_accepts_valid_name() {
187 let name: OriginalName = serde_json::from_str(r#""test-lib""#).unwrap();
188 assert_eq!(name, OriginalName("test-lib".to_string()));
189 }
190
191 #[test]
192 fn deserialize_rejects_invalid_name() {
193 assert!(serde_json::from_str::<OriginalName>(r#""../etc""#).is_err());
196 assert!(serde_json::from_str::<OriginalName>(r#""a/b""#).is_err());
197 assert!(serde_json::from_str::<OriginalName>(r#""..""#).is_err());
198 }
199
200 #[test]
201 fn too_long_package_name() {
202 assert_eq!(
203 OriginalName::try_from(
204 "zbsfrofdgxekytxrporaocoieaviehlvjrroockxufdkzgtxudkmdkentyyhkmtpx"
205 )
206 .unwrap_err(),
207 NameError::InvalidLength
208 );
209 }
210}