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