1#[macro_export]
2macro_rules! custom_string {
3 (
4 $(#[$meta:meta])*,
5 $owned_struct_name:ident,
6 $ref_struct_name:ident,
7 $validate_fn:expr
8 ) => {
9
10 $(#[$meta])*
11 #[derive(Clone, Ord, PartialOrd, Eq, Hash, Debug)]
12 pub struct $owned_struct_name {
13 value: String,
14 }
15
16 $(#[$meta])*
17 #[derive(Copy, Clone, Ord, PartialOrd, Eq, Hash, Debug)]
18 pub struct $ref_struct_name<'a> {
19 value: &'a str,
20 }
21
22 impl<S: AsRef<str>> PartialEq<S> for $owned_struct_name {
23 fn eq(&self, other: &S) -> bool {
24 self.value() == other.as_ref()
25 }
26 }
27
28 impl<'a, S: AsRef<str>> PartialEq<S> for $ref_struct_name<'a> {
29 fn eq(&self, other: &S) -> bool {
30 self.value() == other.as_ref()
31 }
32 }
33
34 impl $owned_struct_name {
35 pub fn validate(value: &str) -> Result<&str, &'static str> {
42 match $validate_fn(value) {
43 Ok(()) => Ok(value),
44 Err(e) => Err(e),
45 }
46 }
47
48 pub fn is_valid(value: &str) -> bool {
50 Self::validate(value).is_ok()
51 }
52 }
53
54 impl<'a> $ref_struct_name<'a> {
55 pub fn validate(value: &str) -> Result<&str, &'static str> {
62 match $validate_fn(value) {
63 Ok(()) => Ok(value),
64 Err(e) => Err(e),
65 }
66 }
67
68 pub fn is_valid(value: &str) -> bool {
70 Self::validate(value).is_ok()
71 }
72 }
73
74 impl $owned_struct_name {
75 #[doc = concat!("Creates a new `", stringify!($owned_struct_name), "` from the `value`.")]
78 #[doc = ""]
79 #[doc = "# Safety"]
80 #[doc = "The `value` must be valid."]
81 pub unsafe fn new_unchecked<S>(value: S) -> Self
82 where
83 S: Into<String>,
84 {
85 let value: String = value.into();
86
87 debug_assert!(Self::is_valid(value.as_str()));
88
89 Self { value }
90 }
91
92 #[doc = concat!("Creates a new `", stringify!($owned_struct_name), "` from the `value`.")]
93 pub fn new<S>(value: S) -> Result<Self, &'static str>
94 where
95 S: AsRef<str> + Into<String>,
96 {
97 Ok(unsafe { Self::new_unchecked(Self::validate(value.as_ref())?) })
98 }
99 }
100
101 impl<'a> $ref_struct_name<'a> {
102 #[doc = concat!("Creates a new `", stringify!($ref_struct_name), "` from the `value`.")]
105 #[doc = ""]
106 #[doc = "# Safety"]
107 #[doc = "The `value` must be valid."]
108 pub unsafe fn new_unchecked(value: &'a str) -> Self {
109 debug_assert!(Self::is_valid(value));
110
111 Self { value }
112 }
113
114 #[doc = concat!("Creates a new `", stringify!($ref_struct_name), "` from the `value`.")]
115 pub fn new(value: &'a str) -> Result<Self, &'static str> {
116 Ok(unsafe { Self::new_unchecked(Self::validate(value)?) })
117 }
118 }
119
120 impl $owned_struct_name {
121 pub fn value(&self) -> &str {
125 self.value.as_str()
126 }
127
128 pub fn len(&self) -> usize {
130 self.value.len()
131 }
132
133 pub fn is_empty(&self) -> bool {
135 self.value.is_empty()
136 }
137 }
138
139 impl<'a> $ref_struct_name<'a> {
140 pub fn value(&self) -> &str {
144 self.value
145 }
146
147 pub fn len(&self) -> usize {
149 self.value.len()
150 }
151
152 pub fn is_empty(&self) -> bool {
154 self.value.is_empty()
155 }
156 }
157
158 impl $owned_struct_name {
159 pub fn to_ref(&self) -> $ref_struct_name {
163 unsafe { $ref_struct_name::new_unchecked(self.value.as_str()) }
164 }
165 }
166
167 impl<'a> $ref_struct_name<'a> {
168 pub fn to_owned(self) -> $owned_struct_name {
172 unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
173 }
174 }
175
176 impl<'a> From<$ref_struct_name<'a>> for $owned_struct_name {
177 fn from(reference: $ref_struct_name<'a>) -> Self {
178 reference.to_owned()
179 }
180 }
181
182 impl From<$owned_struct_name> for String {
183 fn from(value: $owned_struct_name) -> Self {
184 value.value
185 }
186 }
187
188 impl<'a> From<$ref_struct_name<'a>> for String {
189 fn from(value: $ref_struct_name<'a>) -> Self {
190 value.to_string()
191 }
192 }
193
194 impl AsRef<str> for $owned_struct_name {
195 fn as_ref(&self) -> &str {
196 self.value.as_ref()
197 }
198 }
199
200 impl<'a> AsRef<str> for $ref_struct_name<'a> {
201 fn as_ref(&self) -> &str {
202 self.value
203 }
204 }
205
206 impl std::borrow::Borrow<str> for $owned_struct_name {
207 fn borrow(&self) -> &str {
208 self.value.borrow()
209 }
210 }
211
212 impl<'a> std::borrow::Borrow<str> for $ref_struct_name<'a> {
213 fn borrow(&self) -> &str {
214 self.value
215 }
216 }
217
218 impl std::fmt::Display for $owned_struct_name {
219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
220 write!(f, "{}", self.value)
221 }
222 }
223
224 impl<'a> std::fmt::Display for $ref_struct_name<'a> {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 write!(f, "{}", self.value)
227 }
228 }
229 };
230}
231
232#[cfg(test)]
233#[allow(dead_code)]
234mod tests {
235 custom_string!(#[doc = "A lowercase string."], Lower, LowerRef, |s: &str| if !s
236 .as_bytes()
237 .iter()
238 .all(|c| c.is_ascii_lowercase())
239 {
240 Err("not lowercase")
241 } else {
242 Ok(())
243 });
244
245 #[test]
246 fn equals() {
247 let one: Lower = Lower::new("one").unwrap();
248 let two: Lower = Lower::new("two").unwrap();
249
250 assert_eq!(one, "one");
251 assert_eq!(one, one);
252 assert_ne!(one, "two");
253 assert_ne!(one, two);
254 }
255
256 #[test]
257 fn validation() {
258 assert!(Lower::is_valid("one"));
259 assert!(!Lower::is_valid("ONE"));
260
261 assert!(Lower::validate("one").is_ok());
262 assert_eq!(Lower::validate("ONE").err().unwrap(), "not lowercase");
263 }
264}