Skip to main content

custom_string/
custom_string.rs

1#[macro_export]
2macro_rules! custom_string {
3    (
4        $(#[$meta:meta])*,
5        $owned_struct_name:ident,
6        $validate_fn:expr
7    ) => {
8        $crate::__paste! {
9
10            $(#[$meta])*
11            #[derive(Clone, Eq, Ord, PartialOrd, Hash, Debug)]
12            pub struct $owned_struct_name {
13                value: String,
14            }
15
16            $(#[$meta])*
17            #[derive(Copy, Clone, Eq, Ord, PartialOrd, Hash, Debug)]
18            pub struct [<$owned_struct_name Ref>]<'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 [<$owned_struct_name Ref>]<'a> {
29                fn eq(&self, other: &S) -> bool {
30                    self.value() == other.as_ref()
31                }
32            }
33
34            impl<'a> PartialOrd<[<$owned_struct_name Ref>]<'a>> for $owned_struct_name {
35                fn partial_cmp(&self, other: &[<$owned_struct_name Ref>]<'a>) -> Option<std::cmp::Ordering> {
36                    self.value().partial_cmp(other.value())
37                }
38            }
39
40            impl<'a> PartialOrd<$owned_struct_name> for [<$owned_struct_name Ref>]<'a> {
41                fn partial_cmp(&self, other: &$owned_struct_name) -> Option<std::cmp::Ordering> {
42                    self.value().partial_cmp(other.value())
43                }
44            }
45
46            impl $owned_struct_name {
47                //! Validation
48
49                /// Validates the `value`.
50                ///
51                /// Returns `Ok(value)`.
52                /// Returns `Err(error)` if the `value` is invalid.
53                pub fn validate(value: &str) -> Result<&str, $crate::ValidationError> {
54                    match $validate_fn(value) {
55                        Ok(()) => Ok(value),
56                        Err(e) => Err($crate::ValidationError::new(e)),
57                    }
58                }
59
60                /// Checks if the `value` is valid.
61                pub fn is_valid(value: &str) -> bool {
62                    Self::validate(value).is_ok()
63                }
64            }
65
66            impl<'a> [<$owned_struct_name Ref>]<'a> {
67                //! Validation
68
69                /// Validates the `value`.
70                ///
71                /// Returns `Ok(value)`.
72                /// Returns `Err(error)` if the `value` is invalid.
73                pub fn validate(value: &str) -> Result<&str, $crate::ValidationError> {
74                    $owned_struct_name::validate(value)
75                }
76
77                /// Checks if the `value` is valid.
78                pub fn is_valid(value: &str) -> bool {
79                    Self::validate(value).is_ok()
80                }
81            }
82
83            impl $owned_struct_name {
84                //! Construction
85
86                #[doc = concat!("Creates a new [", stringify!($owned_struct_name), "] from the `value`.")]
87                #[doc = ""]
88                #[doc = "# Safety"]
89                #[doc = "The `value` must be valid."]
90                pub unsafe fn new_unchecked<S>(value: S) -> Self
91                where
92                    S: Into<String>,
93                {
94                    let value: String = value.into();
95
96                    debug_assert!(Self::is_valid(value.as_str()));
97
98                    Self { value }
99                }
100
101                #[doc = concat!("Creates a new [", stringify!($owned_struct_name), "] from the `value`.")]
102                pub fn new<S>(value: S) -> Result<Self, $crate::ValidationError>
103                where
104                    S: AsRef<str> + Into<String>,
105                {
106                    Self::validate(value.as_ref())?;
107                    Ok(unsafe { Self::new_unchecked(value) })
108                }
109            }
110
111            impl<'a> [<$owned_struct_name Ref>]<'a> {
112                //! Construction
113
114                #[doc = concat!("Creates a new [", stringify!([<$owned_struct_name Ref>]), "] from the `value`.")]
115                #[doc = ""]
116                #[doc = "# Safety"]
117                #[doc = "The `value` must be valid."]
118                pub unsafe fn new_unchecked(value: &'a str) -> Self {
119                    debug_assert!(Self::is_valid(value));
120
121                    Self { value }
122                }
123
124                #[doc = concat!("Creates a new [", stringify!([<$owned_struct_name Ref>]), "] from the `value`.")]
125                pub fn new(value: &'a str) -> Result<Self, $crate::ValidationError> {
126                    Ok(unsafe { Self::new_unchecked(Self::validate(value)?) })
127                }
128            }
129
130            impl $owned_struct_name {
131                //! Properties
132
133                /// Gets the value.
134                pub fn value(&self) -> &str {
135                    self.value.as_str()
136                }
137
138                /// Gets the length of the value. (in bytes)
139                pub fn len(&self) -> usize {
140                    self.value.len()
141                }
142
143                /// Checks if the value is empty.
144                pub fn is_empty(&self) -> bool {
145                    self.value.is_empty()
146                }
147            }
148
149            impl<'a> [<$owned_struct_name Ref>]<'a> {
150                //! Properties
151
152                /// Gets the value.
153                pub fn value(&self) -> &str {
154                    self.value
155                }
156
157                /// Gets the length of the value. (in bytes)
158                pub fn len(&self) -> usize {
159                    self.value.len()
160                }
161
162                /// Checks if the value is empty.
163                pub fn is_empty(&self) -> bool {
164                    self.value.is_empty()
165                }
166            }
167
168            impl $owned_struct_name {
169                //! Conversions
170
171                /// Converts the owned type to a reference type.
172                #[must_use]
173                pub fn to_ref(&self) -> [<$owned_struct_name Ref>]<'_> {
174                    unsafe { [<$owned_struct_name Ref>]::new_unchecked(self.value.as_str()) }
175                }
176            }
177
178            impl<'a> [<$owned_struct_name Ref>]<'a> {
179                //! Conversions
180
181                /// Converts the reference type to an owned type.
182                #[must_use]
183                pub fn into_owned(self) -> $owned_struct_name {
184                    unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
185                }
186            }
187
188            impl<'a> From<[<$owned_struct_name Ref>]<'a>> for $owned_struct_name {
189                fn from(reference: [<$owned_struct_name Ref>]<'a>) -> Self {
190                    reference.into_owned()
191                }
192            }
193
194            impl From<$owned_struct_name> for String {
195                fn from(value: $owned_struct_name) -> Self {
196                    value.value
197                }
198            }
199
200            impl<'a> From<[<$owned_struct_name Ref>]<'a>> for String {
201                fn from(value: [<$owned_struct_name Ref>]<'a>) -> Self {
202                    value.value.to_owned()
203                }
204            }
205
206            impl TryFrom<String> for $owned_struct_name {
207                type Error = $crate::ValidationError;
208
209                fn try_from(value: String) -> Result<Self, Self::Error> {
210                    Self::new(value)
211                }
212            }
213
214            impl<'a> TryFrom<&'a str> for $owned_struct_name {
215                type Error = $crate::ValidationError;
216
217                fn try_from(value: &'a str) -> Result<Self, Self::Error> {
218                    Self::new(value)
219                }
220            }
221
222            impl<'a> TryFrom<&'a str> for [<$owned_struct_name Ref>]<'a> {
223                type Error = $crate::ValidationError;
224
225                fn try_from(value: &'a str) -> Result<Self, Self::Error> {
226                    Self::new(value)
227                }
228            }
229
230            impl AsRef<str> for $owned_struct_name {
231                fn as_ref(&self) -> &str {
232                    self.value.as_str()
233                }
234            }
235
236            impl<'a> AsRef<str> for [<$owned_struct_name Ref>]<'a> {
237                fn as_ref(&self) -> &str {
238                    self.value
239                }
240            }
241
242            impl std::borrow::Borrow<str> for $owned_struct_name {
243                fn borrow(&self) -> &str {
244                    self.value.as_str()
245                }
246            }
247
248            impl<'a> std::borrow::Borrow<str> for [<$owned_struct_name Ref>]<'a> {
249                fn borrow(&self) -> &str {
250                    self.value
251                }
252            }
253
254            impl std::ops::Deref for $owned_struct_name {
255                type Target = str;
256
257                fn deref(&self) -> &str {
258                    self.value.as_str()
259                }
260            }
261
262            impl<'a> std::ops::Deref for [<$owned_struct_name Ref>]<'a> {
263                type Target = str;
264
265                fn deref(&self) -> &str {
266                    self.value
267                }
268            }
269
270            impl std::fmt::Display for $owned_struct_name {
271                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272                    f.write_str(self.value())
273                }
274            }
275
276            impl<'a> std::fmt::Display for [<$owned_struct_name Ref>]<'a> {
277                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278                    f.write_str(self.value)
279                }
280            }
281
282            impl std::str::FromStr for $owned_struct_name {
283                type Err = $crate::ValidationError;
284
285                fn from_str(s: &str) -> Result<Self, Self::Err> {
286                    Self::new(s)
287                }
288            }
289
290            #[doc = concat!("An element with a [", stringify!($owned_struct_name), "].")]
291            pub trait [<With $owned_struct_name>] {
292                #[doc = concat!("Gets the [", stringify!($owned_struct_name), "].")]
293                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_>;
294            }
295
296            impl [<With $owned_struct_name>] for $owned_struct_name {
297                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_> {
298                    self.to_ref()
299                }
300            }
301
302            impl<'a> [<With $owned_struct_name>] for [<$owned_struct_name Ref>]<'a> {
303                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_> {
304                    *self
305                }
306            }
307
308            $crate::__custom_string_serde_impl!($owned_struct_name, [<$owned_struct_name Ref>]);
309        }
310    };
311}
312
313#[cfg(feature = "serde")]
314#[doc(hidden)]
315#[macro_export]
316macro_rules! __custom_string_serde_impl {
317    ($owned_struct_name:ident, $ref_struct_name:ident) => {
318        impl $crate::__serde::Serialize for $owned_struct_name {
319            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
320            where
321                S: $crate::__serde::Serializer,
322            {
323                $crate::__serde::Serialize::serialize(&self.value, serializer)
324            }
325        }
326
327        impl<'a> $crate::__serde::Serialize for $ref_struct_name<'a> {
328            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
329            where
330                S: $crate::__serde::Serializer,
331            {
332                $crate::__serde::Serialize::serialize(&self.value, serializer)
333            }
334        }
335
336        impl<'de> $crate::__serde::Deserialize<'de> for $owned_struct_name {
337            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
338            where
339                D: $crate::__serde::Deserializer<'de>,
340            {
341                let value: String =
342                    <String as $crate::__serde::Deserialize>::deserialize(deserializer)?;
343                Self::new(value).map_err(<D::Error as $crate::__serde::de::Error>::custom)
344            }
345        }
346    };
347}
348
349#[cfg(not(feature = "serde"))]
350#[doc(hidden)]
351#[macro_export]
352macro_rules! __custom_string_serde_impl {
353    ($owned_struct_name:ident, $ref_struct_name:ident) => {};
354}
355
356#[cfg(test)]
357#[allow(dead_code)]
358mod tests {
359    use std::collections::HashMap;
360
361    custom_string!(
362        #[doc = "A lowercase string."],
363        Lower,
364        |s: &str| if !s.as_bytes().iter().all(|c| c.is_ascii_lowercase()) {
365            Err("not lowercase")
366        } else {
367            Ok(())
368        }
369    );
370
371    #[test]
372    fn validation() {
373        assert!(Lower::is_valid("abc"));
374        assert!(!Lower::is_valid("ABC"));
375        assert!(!Lower::is_valid("aBc"));
376
377        let result: Result<&str, _> = Lower::validate("abc");
378        assert!(result.is_ok());
379
380        let error: crate::ValidationError = Lower::validate("ABC").unwrap_err();
381        assert_eq!(error.message(), "not lowercase");
382        assert_eq!(error.to_string(), "not lowercase");
383    }
384
385    #[test]
386    fn validation_ref() {
387        assert!(LowerRef::is_valid("abc"));
388        assert!(!LowerRef::is_valid("ABC"));
389
390        let error: crate::ValidationError = LowerRef::validate("ABC").unwrap_err();
391        assert_eq!(error.message(), "not lowercase");
392    }
393
394    #[test]
395    fn construction() {
396        let owned: Lower = Lower::new("abc").unwrap();
397        assert_eq!(owned.value(), "abc");
398
399        let error: crate::ValidationError = Lower::new("ABC").unwrap_err();
400        assert_eq!(error.message(), "not lowercase");
401
402        let owned_from_string: Lower = Lower::new(String::from("abc")).unwrap();
403        assert_eq!(owned_from_string.value(), "abc");
404    }
405
406    #[test]
407    fn construction_ref() {
408        let reference: LowerRef = LowerRef::new("abc").unwrap();
409        assert_eq!(reference.value(), "abc");
410
411        let error: crate::ValidationError = LowerRef::new("ABC").unwrap_err();
412        assert_eq!(error.message(), "not lowercase");
413    }
414
415    #[test]
416    fn properties() {
417        let owned: Lower = Lower::new("abc").unwrap();
418        assert_eq!(owned.value(), "abc");
419        assert_eq!(owned.len(), 3);
420        assert!(!owned.is_empty());
421
422        let reference: LowerRef = LowerRef::new("abc").unwrap();
423        assert_eq!(reference.value(), "abc");
424        assert_eq!(reference.len(), 3);
425        assert!(!reference.is_empty());
426    }
427
428    #[test]
429    fn equality() {
430        let one: Lower = Lower::new("one").unwrap();
431        let two: Lower = Lower::new("two").unwrap();
432
433        assert_eq!(one, "one");
434        assert_eq!(one, one);
435        assert_ne!(one, "two");
436        assert_ne!(one, two);
437
438        let one_ref: LowerRef = LowerRef::new("one").unwrap();
439        let two_ref: LowerRef = LowerRef::new("two").unwrap();
440
441        assert_eq!(one_ref, "one");
442        assert_eq!(one_ref, one_ref);
443        assert_ne!(one_ref, "two");
444        assert_ne!(one_ref, two_ref);
445    }
446
447    #[test]
448    fn ordering() {
449        let a: Lower = Lower::new("a").unwrap();
450        let b: Lower = Lower::new("b").unwrap();
451        assert!(a < b);
452
453        let a_ref: LowerRef = LowerRef::new("a").unwrap();
454        let b_ref: LowerRef = LowerRef::new("b").unwrap();
455        assert!(a_ref < b_ref);
456    }
457
458    #[test]
459    fn display() {
460        let owned: Lower = Lower::new("abc").unwrap();
461        assert_eq!(format!("{}", owned), "abc");
462
463        let reference: LowerRef = LowerRef::new("abc").unwrap();
464        assert_eq!(format!("{}", reference), "abc");
465    }
466
467    #[test]
468    fn deref() {
469        let owned: Lower = Lower::new("abc").unwrap();
470        let s: &str = &owned;
471        assert_eq!(s, "abc");
472        assert!(owned.starts_with("ab"));
473
474        let reference: LowerRef = LowerRef::new("abc").unwrap();
475        let s: &str = &reference;
476        assert_eq!(s, "abc");
477        assert!(reference.starts_with("ab"));
478    }
479
480    #[test]
481    fn conversions_owned_ref() {
482        let owned: Lower = Lower::new("abc").unwrap();
483        let reference: LowerRef = owned.to_ref();
484        assert_eq!(reference.value(), "abc");
485
486        let back_to_owned: Lower = reference.into_owned();
487        assert_eq!(back_to_owned.value(), "abc");
488
489        let from_ref: Lower = Lower::from(reference);
490        assert_eq!(from_ref.value(), "abc");
491    }
492
493    #[test]
494    fn conversions_to_string() {
495        let owned: Lower = Lower::new("abc").unwrap();
496        let s: String = String::from(owned);
497        assert_eq!(s, "abc");
498
499        let reference: LowerRef = LowerRef::new("abc").unwrap();
500        let s: String = String::from(reference);
501        assert_eq!(s, "abc");
502    }
503
504    #[test]
505    fn try_from() {
506        let owned: Lower = Lower::try_from("abc").unwrap();
507        assert_eq!(owned.value(), "abc");
508
509        let owned_from_string: Lower = Lower::try_from(String::from("abc")).unwrap();
510        assert_eq!(owned_from_string.value(), "abc");
511
512        let reference: LowerRef = LowerRef::try_from("abc").unwrap();
513        assert_eq!(reference.value(), "abc");
514
515        let error: crate::ValidationError = Lower::try_from("ABC").unwrap_err();
516        assert_eq!(error.message(), "not lowercase");
517    }
518
519    #[test]
520    fn as_ref_and_borrow() {
521        use std::borrow::Borrow;
522
523        let owned: Lower = Lower::new("abc").unwrap();
524        let s: &str = owned.as_ref();
525        assert_eq!(s, "abc");
526        let s: &str = owned.borrow();
527        assert_eq!(s, "abc");
528
529        let reference: LowerRef = LowerRef::new("abc").unwrap();
530        let s: &str = reference.as_ref();
531        assert_eq!(s, "abc");
532        let s: &str = reference.borrow();
533        assert_eq!(s, "abc");
534    }
535
536    #[test]
537    fn hash_map_lookup() {
538        let mut map: HashMap<Lower, i32> = HashMap::new();
539        let key: Lower = Lower::new("abc").unwrap();
540        map.insert(key, 42);
541
542        assert_eq!(map.get("abc"), Some(&42));
543        assert_eq!(map.get("xyz"), None);
544    }
545
546    #[test]
547    fn clone() {
548        let owned: Lower = Lower::new("abc").unwrap();
549        let cloned: Lower = owned.clone();
550        assert_eq!(owned, cloned);
551
552        let reference: LowerRef = LowerRef::new("abc").unwrap();
553        let copied: LowerRef = reference;
554        assert_eq!(reference, copied);
555    }
556
557    #[test]
558    fn cross_type_equality() {
559        let owned: Lower = Lower::new("abc").unwrap();
560        let reference: LowerRef = LowerRef::new("abc").unwrap();
561
562        assert_eq!(owned, reference);
563        assert_eq!(reference, owned);
564
565        let other_ref: LowerRef = LowerRef::new("xyz").unwrap();
566        assert_ne!(owned, other_ref);
567        assert_ne!(other_ref, owned);
568    }
569
570    #[test]
571    fn cross_type_ordering() {
572        let owned_a: Lower = Lower::new("a").unwrap();
573        let ref_b: LowerRef = LowerRef::new("b").unwrap();
574
575        assert!(owned_a < ref_b);
576        assert!(ref_b > owned_a);
577
578        let owned_b: Lower = Lower::new("b").unwrap();
579        let ref_a: LowerRef = LowerRef::new("a").unwrap();
580
581        assert!(owned_b > ref_a);
582        assert!(ref_a < owned_b);
583    }
584
585    #[test]
586    fn from_str() {
587        let owned: Lower = "abc".parse().unwrap();
588        assert_eq!(owned.value(), "abc");
589
590        let error: crate::ValidationError = "ABC".parse::<Lower>().unwrap_err();
591        assert_eq!(error.message(), "not lowercase");
592    }
593
594    #[cfg(feature = "serde")]
595    mod serde_tests {
596        use super::*;
597
598        #[test]
599        fn serialize_owned() {
600            let owned: Lower = Lower::new("abc").unwrap();
601            let json: String = serde_json::to_string(&owned).unwrap();
602            assert_eq!(json, "\"abc\"");
603        }
604
605        #[test]
606        fn serialize_ref() {
607            let reference: LowerRef = LowerRef::new("abc").unwrap();
608            let json: String = serde_json::to_string(&reference).unwrap();
609            assert_eq!(json, "\"abc\"");
610        }
611
612        #[test]
613        fn deserialize_valid() {
614            let owned: Lower = serde_json::from_str("\"abc\"").unwrap();
615            assert_eq!(owned.value(), "abc");
616        }
617
618        #[test]
619        fn deserialize_invalid() {
620            let result: Result<Lower, _> = serde_json::from_str("\"ABC\"");
621            assert!(result.is_err());
622        }
623
624        #[test]
625        fn round_trip() {
626            let original: Lower = Lower::new("abc").unwrap();
627            let json: String = serde_json::to_string(&original).unwrap();
628            let deserialized: Lower = serde_json::from_str(&json).unwrap();
629            assert_eq!(original, deserialized);
630        }
631    }
632
633    #[test]
634    fn validation_error() {
635        let error: crate::ValidationError = crate::ValidationError::new("test error");
636        assert_eq!(error.message(), "test error");
637        assert_eq!(error.to_string(), "test error");
638        assert_eq!(error, crate::ValidationError::new("test error"));
639        assert_ne!(error, crate::ValidationError::new("other error"));
640    }
641}