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 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 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 pub fn validate(value: &str) -> Result<&str, $crate::ValidationError> {
74 $owned_struct_name::validate(value)
75 }
76
77 pub fn is_valid(value: &str) -> bool {
79 Self::validate(value).is_ok()
80 }
81 }
82
83 impl $owned_struct_name {
84 #[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 #[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 pub fn value(&self) -> &str {
135 self.value.as_str()
136 }
137
138 pub fn len(&self) -> usize {
140 self.value.len()
141 }
142
143 pub fn is_empty(&self) -> bool {
145 self.value.is_empty()
146 }
147 }
148
149 impl<'a> [<$owned_struct_name Ref>]<'a> {
150 pub fn value(&self) -> &str {
154 self.value
155 }
156
157 pub fn len(&self) -> usize {
159 self.value.len()
160 }
161
162 pub fn is_empty(&self) -> bool {
164 self.value.is_empty()
165 }
166 }
167
168 impl $owned_struct_name {
169 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 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}