1#[macro_export]
2macro_rules! custom_string {
3    ($owned_struct_name:ident, $ref_struct_name:ident, $validate_fn:expr) => {
4        #[derive(Clone, Ord, PartialOrd, Eq, Hash, Debug)]
5        pub struct $owned_struct_name {
6            value: String,
7        }
8
9        #[derive(Copy, Clone, Ord, PartialOrd, Eq, Hash, Debug)]
10        pub struct $ref_struct_name<'a> {
11            value: &'a str,
12        }
13
14        impl<S: AsRef<str>> PartialEq<S> for $owned_struct_name {
15            fn eq(&self, other: &S) -> bool {
16                self.value() == other.as_ref()
17            }
18        }
19
20        impl<'a, S: AsRef<str>> PartialEq<S> for $ref_struct_name<'a> {
21            fn eq(&self, other: &S) -> bool {
22                self.value() == other.as_ref()
23            }
24        }
25
26        impl $owned_struct_name {
27            pub fn validate(value: &str) -> Result<&str, &'static str> {
28                match $validate_fn(value) {
29                    Ok(()) => Ok(value),
30                    Err(e) => Err(e),
31                }
32            }
33
34            pub fn is_valid(value: &str) -> bool {
35                Self::validate(value).is_ok()
36            }
37        }
38
39        impl<'a> $ref_struct_name<'a> {
40            pub fn validate(value: &str) -> Result<&str, &'static str> {
41                match $validate_fn(value) {
42                    Ok(()) => Ok(value),
43                    Err(e) => Err(e),
44                }
45            }
46
47            pub fn is_valid(value: &str) -> bool {
48                Self::validate(value).is_ok()
49            }
50        }
51
52        impl $owned_struct_name {
53            pub unsafe fn new_unchecked<S>(value: S) -> Self
54            where
55                S: Into<String>,
56            {
57                let value: String = value.into();
58
59                debug_assert!(Self::is_valid(value.as_str()));
60
61                Self { value }
62            }
63
64            pub fn new<S>(value: S) -> Result<Self, &'static str>
65            where
66                S: AsRef<str> + Into<String>,
67            {
68                Ok(unsafe { Self::new_unchecked(Self::validate(value.as_ref())?) })
69            }
70        }
71
72        impl<'a> $ref_struct_name<'a> {
73            pub unsafe fn new_unchecked(value: &'a str) -> Self {
74                debug_assert!(Self::is_valid(value));
75
76                Self { value }
77            }
78
79            pub fn new(value: &'a str) -> Result<Self, &'static str> {
80                Ok(unsafe { Self::new_unchecked(Self::validate(value)?) })
81            }
82        }
83
84        impl $owned_struct_name {
85            pub fn value(&self) -> &str {
86                self.value.as_str()
87            }
88
89            pub fn len(&self) -> usize {
90                self.value.len()
91            }
92
93            pub fn is_empty(&self) -> bool {
94                self.value.is_empty()
95            }
96        }
97
98        impl<'a> $ref_struct_name<'a> {
99            pub fn value(&self) -> &'a str {
100                self.value
101            }
102
103            pub fn len(&self) -> usize {
104                self.value.len()
105            }
106
107            pub fn is_empty(&self) -> bool {
108                self.value.is_empty()
109            }
110        }
111
112        impl $owned_struct_name {
113            pub fn to_ref<'a>(&'a self) -> $ref_struct_name<'a> {
114                unsafe { $ref_struct_name::new_unchecked(self.value.as_str()) }
115            }
116        }
117
118        impl<'a> $ref_struct_name<'a> {
119            pub fn to_owned(&self) -> $owned_struct_name {
120                unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
121            }
122        }
123
124        impl AsRef<str> for $owned_struct_name {
125            fn as_ref(&self) -> &str {
126                self.value.as_ref()
127            }
128        }
129
130        impl<'a> AsRef<str> for $ref_struct_name<'a> {
131            fn as_ref(&self) -> &str {
132                self.value
133            }
134        }
135
136        impl Into<String> for $owned_struct_name {
137            fn into(self) -> String {
138                self.value
139            }
140        }
141
142        impl<'a> Into<String> for $ref_struct_name<'a> {
143            fn into(self) -> String {
144                self.value.into()
145            }
146        }
147
148        impl<'a> Into<$owned_struct_name> for $ref_struct_name<'a> {
149            fn into(self) -> $owned_struct_name {
150                self.to_owned()
151            }
152        }
153
154        impl std::fmt::Display for $owned_struct_name {
155            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156                write!(f, "{}", self.value)
157            }
158        }
159
160        impl<'a> std::fmt::Display for $ref_struct_name<'a> {
161            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162                write!(f, "{}", self.value)
163            }
164        }
165    };
166}
167
168#[cfg(test)]
169#[allow(dead_code)]
170mod tests {
171    custom_string!(Lower, LowerRef, |s: &str| if !s
172        .as_bytes()
173        .iter()
174        .all(|c| c.is_ascii_lowercase())
175    {
176        Err("not lowercase")
177    } else {
178        Ok(())
179    });
180
181    #[test]
182    fn equals() {
183        let one: Lower = Lower::new("one").unwrap();
184        let two: Lower = Lower::new("two").unwrap();
185
186        assert_eq!(one, "one");
187        assert_eq!(one, one);
188        assert_ne!(one, "two");
189        assert_ne!(one, two);
190    }
191
192    #[test]
193    fn validation() {
194        assert!(Lower::is_valid("one"));
195        assert!(!Lower::is_valid("ONE"));
196
197        assert!(Lower::validate("one").is_ok());
198        assert_eq!(Lower::validate("ONE").err().unwrap(), "not lowercase");
199    }
200}