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 #[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 #[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}