kellnr_common/
name_or_description.rs1use std::ops::Deref;
2
3use thiserror::Error;
4use utoipa::ToSchema;
5
6#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, Hash, ToSchema)]
13#[schema(value_type = String)]
14#[serde(try_from = "String")]
19pub struct NameOrDescription(String);
20
21const MAX_LEN: usize = 64;
23
24#[derive(Debug, PartialEq, Eq, Error)]
25pub enum NameOrDescriptionError {
26 #[error("Search query must not be longer than {MAX_LEN} characters")]
27 InvalidLength,
28}
29
30impl NameOrDescription {
31 pub fn as_str(&self) -> &str {
32 &self.0
33 }
34
35 pub fn into_inner(self) -> String {
36 self.0
37 }
38}
39
40impl TryFrom<String> for NameOrDescription {
41 type Error = NameOrDescriptionError;
42
43 fn try_from(value: String) -> Result<Self, Self::Error> {
44 if value.chars().count() > MAX_LEN {
45 Err(NameOrDescriptionError::InvalidLength)
46 } else {
47 Ok(Self(value))
48 }
49 }
50}
51
52impl TryFrom<&str> for NameOrDescription {
53 type Error = NameOrDescriptionError;
54
55 fn try_from(value: &str) -> Result<Self, Self::Error> {
56 Self::try_from(value.to_string())
57 }
58}
59
60impl From<NameOrDescription> for String {
61 fn from(value: NameOrDescription) -> Self {
62 value.0
63 }
64}
65
66impl Deref for NameOrDescription {
67 type Target = String;
68
69 fn deref(&self) -> &Self::Target {
70 &self.0
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn accepts_arbitrary_text() {
80 assert_eq!(
81 NameOrDescription::try_from("vcard parser")
82 .unwrap()
83 .as_str(),
84 "vcard parser"
85 );
86 }
87
88 #[test]
89 fn accepts_query_at_the_limit() {
90 let query = "a".repeat(MAX_LEN);
91 assert_eq!(
92 NameOrDescription::try_from(query.clone()).unwrap().as_str(),
93 query
94 );
95 }
96
97 #[test]
98 fn rejects_query_over_the_limit() {
99 let query = "a".repeat(MAX_LEN + 1);
100 assert_eq!(
101 NameOrDescription::try_from(query),
102 Err(NameOrDescriptionError::InvalidLength)
103 );
104 }
105
106 #[test]
109 fn counts_characters_not_bytes() {
110 let query = "รค".repeat(MAX_LEN);
111 assert!(query.len() > MAX_LEN);
112 assert!(NameOrDescription::try_from(query).is_ok());
113 }
114
115 #[test]
118 fn deserialization_enforces_the_limit() {
119 let ok =
120 serde_json::from_value::<NameOrDescription>(serde_json::json!("a".repeat(MAX_LEN)));
121 assert!(ok.is_ok());
122
123 let too_long =
124 serde_json::from_value::<NameOrDescription>(serde_json::json!("a".repeat(MAX_LEN + 1)));
125 assert!(too_long.is_err());
126 }
127}