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                pub fn to_ref(&self) -> [<$owned_struct_name Ref>]<'_> {
173                    unsafe { [<$owned_struct_name Ref>]::new_unchecked(self.value.as_str()) }
174                }
175            }
176
177            impl<'a> [<$owned_struct_name Ref>]<'a> {
178                //! Conversions
179
180                /// Converts the reference type to an owned type.
181                pub fn to_owned(self) -> $owned_struct_name {
182                    unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
183                }
184            }
185
186            impl<'a> From<[<$owned_struct_name Ref>]<'a>> for $owned_struct_name {
187                fn from(reference: [<$owned_struct_name Ref>]<'a>) -> Self {
188                    reference.to_owned()
189                }
190            }
191
192            impl From<$owned_struct_name> for String {
193                fn from(value: $owned_struct_name) -> Self {
194                    value.value
195                }
196            }
197
198            impl<'a> From<[<$owned_struct_name Ref>]<'a>> for String {
199                fn from(value: [<$owned_struct_name Ref>]<'a>) -> Self {
200                    value.value.to_owned()
201                }
202            }
203
204            impl TryFrom<String> for $owned_struct_name {
205                type Error = $crate::ValidationError;
206
207                fn try_from(value: String) -> Result<Self, Self::Error> {
208                    Self::new(value)
209                }
210            }
211
212            impl<'a> TryFrom<&'a str> for $owned_struct_name {
213                type Error = $crate::ValidationError;
214
215                fn try_from(value: &'a str) -> Result<Self, Self::Error> {
216                    Self::new(value)
217                }
218            }
219
220            impl<'a> TryFrom<&'a str> for [<$owned_struct_name Ref>]<'a> {
221                type Error = $crate::ValidationError;
222
223                fn try_from(value: &'a str) -> Result<Self, Self::Error> {
224                    Self::new(value)
225                }
226            }
227
228            impl AsRef<str> for $owned_struct_name {
229                fn as_ref(&self) -> &str {
230                    self.value.as_str()
231                }
232            }
233
234            impl<'a> AsRef<str> for [<$owned_struct_name Ref>]<'a> {
235                fn as_ref(&self) -> &str {
236                    self.value
237                }
238            }
239
240            impl std::borrow::Borrow<str> for $owned_struct_name {
241                fn borrow(&self) -> &str {
242                    self.value.as_str()
243                }
244            }
245
246            impl<'a> std::borrow::Borrow<str> for [<$owned_struct_name Ref>]<'a> {
247                fn borrow(&self) -> &str {
248                    self.value
249                }
250            }
251
252            impl std::ops::Deref for $owned_struct_name {
253                type Target = str;
254
255                fn deref(&self) -> &str {
256                    self.value.as_str()
257                }
258            }
259
260            impl<'a> std::ops::Deref for [<$owned_struct_name Ref>]<'a> {
261                type Target = str;
262
263                fn deref(&self) -> &str {
264                    self.value
265                }
266            }
267
268            impl std::fmt::Display for $owned_struct_name {
269                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270                    f.write_str(self.value())
271                }
272            }
273
274            impl<'a> std::fmt::Display for [<$owned_struct_name Ref>]<'a> {
275                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276                    f.write_str(self.value)
277                }
278            }
279
280            impl std::str::FromStr for $owned_struct_name {
281                type Err = $crate::ValidationError;
282
283                fn from_str(s: &str) -> Result<Self, Self::Err> {
284                    Self::new(s)
285                }
286            }
287
288            #[doc = concat!("An element with a [", stringify!($owned_struct_name), "].")]
289            pub trait [<With $owned_struct_name>] {
290                #[doc = concat!("Gets the [", stringify!($owned_struct_name), "].")]
291                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_>;
292            }
293
294            impl [<With $owned_struct_name>] for $owned_struct_name {
295                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_> {
296                    self.to_ref()
297                }
298            }
299
300            impl<'a> [<With $owned_struct_name>] for [<$owned_struct_name Ref>]<'a> {
301                fn [<$owned_struct_name:snake>](&self) -> [<$owned_struct_name Ref>]<'_> {
302                    *self
303                }
304            }
305
306            $crate::__custom_string_serde_impl!($owned_struct_name, [<$owned_struct_name Ref>]);
307        }
308    };
309}
310
311#[cfg(feature = "serde")]
312#[doc(hidden)]
313#[macro_export]
314macro_rules! __custom_string_serde_impl {
315    ($owned_struct_name:ident, $ref_struct_name:ident) => {
316        impl $crate::__serde::Serialize for $owned_struct_name {
317            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
318            where
319                S: $crate::__serde::Serializer,
320            {
321                $crate::__serde::Serialize::serialize(&self.value, serializer)
322            }
323        }
324
325        impl<'a> $crate::__serde::Serialize for $ref_struct_name<'a> {
326            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
327            where
328                S: $crate::__serde::Serializer,
329            {
330                $crate::__serde::Serialize::serialize(&self.value, serializer)
331            }
332        }
333
334        impl<'de> $crate::__serde::Deserialize<'de> for $owned_struct_name {
335            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336            where
337                D: $crate::__serde::Deserializer<'de>,
338            {
339                let value: String =
340                    <String as $crate::__serde::Deserialize>::deserialize(deserializer)?;
341                Self::new(value).map_err(<D::Error as $crate::__serde::de::Error>::custom)
342            }
343        }
344    };
345}
346
347#[cfg(not(feature = "serde"))]
348#[doc(hidden)]
349#[macro_export]
350macro_rules! __custom_string_serde_impl {
351    ($owned_struct_name:ident, $ref_struct_name:ident) => {};
352}
353
354#[cfg(test)]
355#[allow(dead_code)]
356mod tests {
357    use std::collections::HashMap;
358
359    custom_string!(
360        #[doc = "A lowercase string."],
361        Lower,
362        |s: &str| if !s.as_bytes().iter().all(|c| c.is_ascii_lowercase()) {
363            Err("not lowercase")
364        } else {
365            Ok(())
366        }
367    );
368
369    #[test]
370    fn validation() {
371        assert!(Lower::is_valid("abc"));
372        assert!(!Lower::is_valid("ABC"));
373        assert!(!Lower::is_valid("aBc"));
374
375        let result: Result<&str, _> = Lower::validate("abc");
376        assert!(result.is_ok());
377
378        let error: crate::ValidationError = Lower::validate("ABC").unwrap_err();
379        assert_eq!(error.message(), "not lowercase");
380        assert_eq!(error.to_string(), "not lowercase");
381    }
382
383    #[test]
384    fn validation_ref() {
385        assert!(LowerRef::is_valid("abc"));
386        assert!(!LowerRef::is_valid("ABC"));
387
388        let error: crate::ValidationError = LowerRef::validate("ABC").unwrap_err();
389        assert_eq!(error.message(), "not lowercase");
390    }
391
392    #[test]
393    fn construction() {
394        let owned: Lower = Lower::new("abc").unwrap();
395        assert_eq!(owned.value(), "abc");
396
397        let error: crate::ValidationError = Lower::new("ABC").unwrap_err();
398        assert_eq!(error.message(), "not lowercase");
399
400        let owned_from_string: Lower = Lower::new(String::from("abc")).unwrap();
401        assert_eq!(owned_from_string.value(), "abc");
402    }
403
404    #[test]
405    fn construction_ref() {
406        let reference: LowerRef = LowerRef::new("abc").unwrap();
407        assert_eq!(reference.value(), "abc");
408
409        let error: crate::ValidationError = LowerRef::new("ABC").unwrap_err();
410        assert_eq!(error.message(), "not lowercase");
411    }
412
413    #[test]
414    fn properties() {
415        let owned: Lower = Lower::new("abc").unwrap();
416        assert_eq!(owned.value(), "abc");
417        assert_eq!(owned.len(), 3);
418        assert!(!owned.is_empty());
419
420        let reference: LowerRef = LowerRef::new("abc").unwrap();
421        assert_eq!(reference.value(), "abc");
422        assert_eq!(reference.len(), 3);
423        assert!(!reference.is_empty());
424    }
425
426    #[test]
427    fn equality() {
428        let one: Lower = Lower::new("one").unwrap();
429        let two: Lower = Lower::new("two").unwrap();
430
431        assert_eq!(one, "one");
432        assert_eq!(one, one);
433        assert_ne!(one, "two");
434        assert_ne!(one, two);
435
436        let one_ref: LowerRef = LowerRef::new("one").unwrap();
437        let two_ref: LowerRef = LowerRef::new("two").unwrap();
438
439        assert_eq!(one_ref, "one");
440        assert_eq!(one_ref, one_ref);
441        assert_ne!(one_ref, "two");
442        assert_ne!(one_ref, two_ref);
443    }
444
445    #[test]
446    fn ordering() {
447        let a: Lower = Lower::new("a").unwrap();
448        let b: Lower = Lower::new("b").unwrap();
449        assert!(a < b);
450
451        let a_ref: LowerRef = LowerRef::new("a").unwrap();
452        let b_ref: LowerRef = LowerRef::new("b").unwrap();
453        assert!(a_ref < b_ref);
454    }
455
456    #[test]
457    fn display() {
458        let owned: Lower = Lower::new("abc").unwrap();
459        assert_eq!(format!("{}", owned), "abc");
460
461        let reference: LowerRef = LowerRef::new("abc").unwrap();
462        assert_eq!(format!("{}", reference), "abc");
463    }
464
465    #[test]
466    fn deref() {
467        let owned: Lower = Lower::new("abc").unwrap();
468        let s: &str = &owned;
469        assert_eq!(s, "abc");
470        assert!(owned.starts_with("ab"));
471
472        let reference: LowerRef = LowerRef::new("abc").unwrap();
473        let s: &str = &reference;
474        assert_eq!(s, "abc");
475        assert!(reference.starts_with("ab"));
476    }
477
478    #[test]
479    fn conversions_owned_ref() {
480        let owned: Lower = Lower::new("abc").unwrap();
481        let reference: LowerRef = owned.to_ref();
482        assert_eq!(reference.value(), "abc");
483
484        let back_to_owned: Lower = reference.to_owned();
485        assert_eq!(back_to_owned.value(), "abc");
486
487        let from_ref: Lower = Lower::from(reference);
488        assert_eq!(from_ref.value(), "abc");
489    }
490
491    #[test]
492    fn conversions_to_string() {
493        let owned: Lower = Lower::new("abc").unwrap();
494        let s: String = String::from(owned);
495        assert_eq!(s, "abc");
496
497        let reference: LowerRef = LowerRef::new("abc").unwrap();
498        let s: String = String::from(reference);
499        assert_eq!(s, "abc");
500    }
501
502    #[test]
503    fn try_from() {
504        let owned: Lower = Lower::try_from("abc").unwrap();
505        assert_eq!(owned.value(), "abc");
506
507        let owned_from_string: Lower = Lower::try_from(String::from("abc")).unwrap();
508        assert_eq!(owned_from_string.value(), "abc");
509
510        let reference: LowerRef = LowerRef::try_from("abc").unwrap();
511        assert_eq!(reference.value(), "abc");
512
513        let error: crate::ValidationError = Lower::try_from("ABC").unwrap_err();
514        assert_eq!(error.message(), "not lowercase");
515    }
516
517    #[test]
518    fn as_ref_and_borrow() {
519        use std::borrow::Borrow;
520
521        let owned: Lower = Lower::new("abc").unwrap();
522        let s: &str = owned.as_ref();
523        assert_eq!(s, "abc");
524        let s: &str = owned.borrow();
525        assert_eq!(s, "abc");
526
527        let reference: LowerRef = LowerRef::new("abc").unwrap();
528        let s: &str = reference.as_ref();
529        assert_eq!(s, "abc");
530        let s: &str = reference.borrow();
531        assert_eq!(s, "abc");
532    }
533
534    #[test]
535    fn hash_map_lookup() {
536        let mut map: HashMap<Lower, i32> = HashMap::new();
537        let key: Lower = Lower::new("abc").unwrap();
538        map.insert(key, 42);
539
540        assert_eq!(map.get("abc"), Some(&42));
541        assert_eq!(map.get("xyz"), None);
542    }
543
544    #[test]
545    fn clone() {
546        let owned: Lower = Lower::new("abc").unwrap();
547        let cloned: Lower = owned.clone();
548        assert_eq!(owned, cloned);
549
550        let reference: LowerRef = LowerRef::new("abc").unwrap();
551        let copied: LowerRef = reference;
552        assert_eq!(reference, copied);
553    }
554
555    #[test]
556    fn cross_type_equality() {
557        let owned: Lower = Lower::new("abc").unwrap();
558        let reference: LowerRef = LowerRef::new("abc").unwrap();
559
560        assert_eq!(owned, reference);
561        assert_eq!(reference, owned);
562
563        let other_ref: LowerRef = LowerRef::new("xyz").unwrap();
564        assert_ne!(owned, other_ref);
565        assert_ne!(other_ref, owned);
566    }
567
568    #[test]
569    fn cross_type_ordering() {
570        let owned_a: Lower = Lower::new("a").unwrap();
571        let ref_b: LowerRef = LowerRef::new("b").unwrap();
572
573        assert!(owned_a < ref_b);
574        assert!(ref_b > owned_a);
575
576        let owned_b: Lower = Lower::new("b").unwrap();
577        let ref_a: LowerRef = LowerRef::new("a").unwrap();
578
579        assert!(owned_b > ref_a);
580        assert!(ref_a < owned_b);
581    }
582
583    #[test]
584    fn from_str() {
585        let owned: Lower = "abc".parse().unwrap();
586        assert_eq!(owned.value(), "abc");
587
588        let error: crate::ValidationError = "ABC".parse::<Lower>().unwrap_err();
589        assert_eq!(error.message(), "not lowercase");
590    }
591
592    #[cfg(feature = "serde")]
593    mod serde_tests {
594        use super::*;
595
596        #[test]
597        fn serialize_owned() {
598            let owned: Lower = Lower::new("abc").unwrap();
599            let json: String = serde_json::to_string(&owned).unwrap();
600            assert_eq!(json, "\"abc\"");
601        }
602
603        #[test]
604        fn serialize_ref() {
605            let reference: LowerRef = LowerRef::new("abc").unwrap();
606            let json: String = serde_json::to_string(&reference).unwrap();
607            assert_eq!(json, "\"abc\"");
608        }
609
610        #[test]
611        fn deserialize_valid() {
612            let owned: Lower = serde_json::from_str("\"abc\"").unwrap();
613            assert_eq!(owned.value(), "abc");
614        }
615
616        #[test]
617        fn deserialize_invalid() {
618            let result: Result<Lower, _> = serde_json::from_str("\"ABC\"");
619            assert!(result.is_err());
620        }
621
622        #[test]
623        fn round_trip() {
624            let original: Lower = Lower::new("abc").unwrap();
625            let json: String = serde_json::to_string(&original).unwrap();
626            let deserialized: Lower = serde_json::from_str(&json).unwrap();
627            assert_eq!(original, deserialized);
628        }
629    }
630
631    #[test]
632    fn validation_error() {
633        let error: crate::ValidationError = crate::ValidationError::new("test error");
634        assert_eq!(error.message(), "test error");
635        assert_eq!(error.to_string(), "test error");
636        assert_eq!(error, crate::ValidationError::new("test error"));
637        assert_ne!(error, crate::ValidationError::new("other error"));
638    }
639}