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