1#[macro_export]
2macro_rules! custom_string {
3 (
4 $(#[$meta:meta])*,
5 $owned_struct_name:ident,
6 $validate_fn:expr
7 ) => {
8 paste::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 #[cfg(feature = "serde")]
289 impl serde::Serialize for $owned_struct_name {
290 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
291 where
292 S: serde::Serializer,
293 {
294 self.value.serialize(serializer)
295 }
296 }
297
298 #[cfg(feature = "serde")]
299 impl<'a> serde::Serialize for [<$owned_struct_name Ref>]<'a> {
300 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301 where
302 S: serde::Serializer,
303 {
304 self.value.serialize(serializer)
305 }
306 }
307
308 #[cfg(feature = "serde")]
309 impl<'de> serde::Deserialize<'de> for $owned_struct_name {
310 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311 where
312 D: serde::Deserializer<'de>,
313 {
314 let value: String = String::deserialize(deserializer)?;
315 Self::new(value).map_err(serde::de::Error::custom)
316 }
317 }
318
319 }
320 };
321}
322
323#[cfg(test)]
324#[allow(dead_code)]
325mod tests {
326 use std::collections::HashMap;
327
328 custom_string!(
329 #[doc = "A lowercase string."],
330 Lower,
331 |s: &str| if !s.as_bytes().iter().all(|c| c.is_ascii_lowercase()) {
332 Err("not lowercase")
333 } else {
334 Ok(())
335 }
336 );
337
338 #[test]
339 fn validation() {
340 assert!(Lower::is_valid("abc"));
341 assert!(!Lower::is_valid("ABC"));
342 assert!(!Lower::is_valid("aBc"));
343
344 let result: Result<&str, _> = Lower::validate("abc");
345 assert!(result.is_ok());
346
347 let error: crate::ValidationError = Lower::validate("ABC").unwrap_err();
348 assert_eq!(error.message(), "not lowercase");
349 assert_eq!(error.to_string(), "not lowercase");
350 }
351
352 #[test]
353 fn validation_ref() {
354 assert!(LowerRef::is_valid("abc"));
355 assert!(!LowerRef::is_valid("ABC"));
356
357 let error: crate::ValidationError = LowerRef::validate("ABC").unwrap_err();
358 assert_eq!(error.message(), "not lowercase");
359 }
360
361 #[test]
362 fn construction() {
363 let owned: Lower = Lower::new("abc").unwrap();
364 assert_eq!(owned.value(), "abc");
365
366 let error: crate::ValidationError = Lower::new("ABC").unwrap_err();
367 assert_eq!(error.message(), "not lowercase");
368
369 let owned_from_string: Lower = Lower::new(String::from("abc")).unwrap();
370 assert_eq!(owned_from_string.value(), "abc");
371 }
372
373 #[test]
374 fn construction_ref() {
375 let reference: LowerRef = LowerRef::new("abc").unwrap();
376 assert_eq!(reference.value(), "abc");
377
378 let error: crate::ValidationError = LowerRef::new("ABC").unwrap_err();
379 assert_eq!(error.message(), "not lowercase");
380 }
381
382 #[test]
383 fn properties() {
384 let owned: Lower = Lower::new("abc").unwrap();
385 assert_eq!(owned.value(), "abc");
386 assert_eq!(owned.len(), 3);
387 assert!(!owned.is_empty());
388
389 let reference: LowerRef = LowerRef::new("abc").unwrap();
390 assert_eq!(reference.value(), "abc");
391 assert_eq!(reference.len(), 3);
392 assert!(!reference.is_empty());
393 }
394
395 #[test]
396 fn equality() {
397 let one: Lower = Lower::new("one").unwrap();
398 let two: Lower = Lower::new("two").unwrap();
399
400 assert_eq!(one, "one");
401 assert_eq!(one, one);
402 assert_ne!(one, "two");
403 assert_ne!(one, two);
404
405 let one_ref: LowerRef = LowerRef::new("one").unwrap();
406 let two_ref: LowerRef = LowerRef::new("two").unwrap();
407
408 assert_eq!(one_ref, "one");
409 assert_eq!(one_ref, one_ref);
410 assert_ne!(one_ref, "two");
411 assert_ne!(one_ref, two_ref);
412 }
413
414 #[test]
415 fn ordering() {
416 let a: Lower = Lower::new("a").unwrap();
417 let b: Lower = Lower::new("b").unwrap();
418 assert!(a < b);
419
420 let a_ref: LowerRef = LowerRef::new("a").unwrap();
421 let b_ref: LowerRef = LowerRef::new("b").unwrap();
422 assert!(a_ref < b_ref);
423 }
424
425 #[test]
426 fn display() {
427 let owned: Lower = Lower::new("abc").unwrap();
428 assert_eq!(format!("{}", owned), "abc");
429
430 let reference: LowerRef = LowerRef::new("abc").unwrap();
431 assert_eq!(format!("{}", reference), "abc");
432 }
433
434 #[test]
435 fn deref() {
436 let owned: Lower = Lower::new("abc").unwrap();
437 let s: &str = &owned;
438 assert_eq!(s, "abc");
439 assert!(owned.starts_with("ab"));
440
441 let reference: LowerRef = LowerRef::new("abc").unwrap();
442 let s: &str = &reference;
443 assert_eq!(s, "abc");
444 assert!(reference.starts_with("ab"));
445 }
446
447 #[test]
448 fn conversions_owned_ref() {
449 let owned: Lower = Lower::new("abc").unwrap();
450 let reference: LowerRef = owned.to_ref();
451 assert_eq!(reference.value(), "abc");
452
453 let back_to_owned: Lower = reference.to_owned();
454 assert_eq!(back_to_owned.value(), "abc");
455
456 let from_ref: Lower = Lower::from(reference);
457 assert_eq!(from_ref.value(), "abc");
458 }
459
460 #[test]
461 fn conversions_to_string() {
462 let owned: Lower = Lower::new("abc").unwrap();
463 let s: String = String::from(owned);
464 assert_eq!(s, "abc");
465
466 let reference: LowerRef = LowerRef::new("abc").unwrap();
467 let s: String = String::from(reference);
468 assert_eq!(s, "abc");
469 }
470
471 #[test]
472 fn try_from() {
473 let owned: Lower = Lower::try_from("abc").unwrap();
474 assert_eq!(owned.value(), "abc");
475
476 let owned_from_string: Lower = Lower::try_from(String::from("abc")).unwrap();
477 assert_eq!(owned_from_string.value(), "abc");
478
479 let reference: LowerRef = LowerRef::try_from("abc").unwrap();
480 assert_eq!(reference.value(), "abc");
481
482 let error: crate::ValidationError = Lower::try_from("ABC").unwrap_err();
483 assert_eq!(error.message(), "not lowercase");
484 }
485
486 #[test]
487 fn as_ref_and_borrow() {
488 use std::borrow::Borrow;
489
490 let owned: Lower = Lower::new("abc").unwrap();
491 let s: &str = owned.as_ref();
492 assert_eq!(s, "abc");
493 let s: &str = owned.borrow();
494 assert_eq!(s, "abc");
495
496 let reference: LowerRef = LowerRef::new("abc").unwrap();
497 let s: &str = reference.as_ref();
498 assert_eq!(s, "abc");
499 let s: &str = reference.borrow();
500 assert_eq!(s, "abc");
501 }
502
503 #[test]
504 fn hash_map_lookup() {
505 let mut map: HashMap<Lower, i32> = HashMap::new();
506 let key: Lower = Lower::new("abc").unwrap();
507 map.insert(key, 42);
508
509 assert_eq!(map.get("abc"), Some(&42));
510 assert_eq!(map.get("xyz"), None);
511 }
512
513 #[test]
514 fn clone() {
515 let owned: Lower = Lower::new("abc").unwrap();
516 let cloned: Lower = owned.clone();
517 assert_eq!(owned, cloned);
518
519 let reference: LowerRef = LowerRef::new("abc").unwrap();
520 let copied: LowerRef = reference;
521 assert_eq!(reference, copied);
522 }
523
524 #[test]
525 fn cross_type_equality() {
526 let owned: Lower = Lower::new("abc").unwrap();
527 let reference: LowerRef = LowerRef::new("abc").unwrap();
528
529 assert_eq!(owned, reference);
530 assert_eq!(reference, owned);
531
532 let other_ref: LowerRef = LowerRef::new("xyz").unwrap();
533 assert_ne!(owned, other_ref);
534 assert_ne!(other_ref, owned);
535 }
536
537 #[test]
538 fn cross_type_ordering() {
539 let owned_a: Lower = Lower::new("a").unwrap();
540 let ref_b: LowerRef = LowerRef::new("b").unwrap();
541
542 assert!(owned_a < ref_b);
543 assert!(ref_b > owned_a);
544
545 let owned_b: Lower = Lower::new("b").unwrap();
546 let ref_a: LowerRef = LowerRef::new("a").unwrap();
547
548 assert!(owned_b > ref_a);
549 assert!(ref_a < owned_b);
550 }
551
552 #[test]
553 fn from_str() {
554 let owned: Lower = "abc".parse().unwrap();
555 assert_eq!(owned.value(), "abc");
556
557 let error: crate::ValidationError = "ABC".parse::<Lower>().unwrap_err();
558 assert_eq!(error.message(), "not lowercase");
559 }
560
561 #[cfg(feature = "serde")]
562 mod serde_tests {
563 use super::*;
564
565 #[test]
566 fn serialize_owned() {
567 let owned: Lower = Lower::new("abc").unwrap();
568 let json: String = serde_json::to_string(&owned).unwrap();
569 assert_eq!(json, "\"abc\"");
570 }
571
572 #[test]
573 fn serialize_ref() {
574 let reference: LowerRef = LowerRef::new("abc").unwrap();
575 let json: String = serde_json::to_string(&reference).unwrap();
576 assert_eq!(json, "\"abc\"");
577 }
578
579 #[test]
580 fn deserialize_valid() {
581 let owned: Lower = serde_json::from_str("\"abc\"").unwrap();
582 assert_eq!(owned.value(), "abc");
583 }
584
585 #[test]
586 fn deserialize_invalid() {
587 let result: Result<Lower, _> = serde_json::from_str("\"ABC\"");
588 assert!(result.is_err());
589 }
590
591 #[test]
592 fn round_trip() {
593 let original: Lower = Lower::new("abc").unwrap();
594 let json: String = serde_json::to_string(&original).unwrap();
595 let deserialized: Lower = serde_json::from_str(&json).unwrap();
596 assert_eq!(original, deserialized);
597 }
598 }
599
600 #[test]
601 fn validation_error() {
602 let error: crate::ValidationError = crate::ValidationError::new("test error");
603 assert_eq!(error.message(), "test error");
604 assert_eq!(error.to_string(), "test error");
605 assert_eq!(error, crate::ValidationError::new("test error"));
606 assert_ne!(error, crate::ValidationError::new("other error"));
607 }
608}