1#[macro_export]
2macro_rules! custom_string {
3 ($owned_struct_name:ident, $ref_struct_name:ident, $validate_fn:expr) => {
4 #[derive(Clone, Ord, PartialOrd, Eq, Hash, Debug)]
5 pub struct $owned_struct_name {
6 value: String,
7 }
8
9 #[derive(Copy, Clone, Ord, PartialOrd, Eq, Hash, Debug)]
10 pub struct $ref_struct_name<'a> {
11 value: &'a str,
12 }
13
14 impl<S: AsRef<str>> PartialEq<S> for $owned_struct_name {
15 fn eq(&self, other: &S) -> bool {
16 self.value() == other.as_ref()
17 }
18 }
19
20 impl<'a, S: AsRef<str>> PartialEq<S> for $ref_struct_name<'a> {
21 fn eq(&self, other: &S) -> bool {
22 self.value() == other.as_ref()
23 }
24 }
25
26 impl $owned_struct_name {
27 pub fn validate(value: &str) -> Result<&str, &'static str> {
28 match $validate_fn(value) {
29 Ok(()) => Ok(value),
30 Err(e) => Err(e),
31 }
32 }
33
34 pub fn is_valid(value: &str) -> bool {
35 Self::validate(value).is_ok()
36 }
37 }
38
39 impl<'a> $ref_struct_name<'a> {
40 pub fn validate(value: &str) -> Result<&str, &'static str> {
41 match $validate_fn(value) {
42 Ok(()) => Ok(value),
43 Err(e) => Err(e),
44 }
45 }
46
47 pub fn is_valid(value: &str) -> bool {
48 Self::validate(value).is_ok()
49 }
50 }
51
52 impl $owned_struct_name {
53 pub unsafe fn new_unchecked<S>(value: S) -> Self
54 where
55 S: Into<String>,
56 {
57 let value: String = value.into();
58
59 debug_assert!(Self::is_valid(value.as_str()));
60
61 Self { value }
62 }
63
64 pub fn new<S>(value: S) -> Result<Self, &'static str>
65 where
66 S: AsRef<str> + Into<String>,
67 {
68 Ok(unsafe { Self::new_unchecked(Self::validate(value.as_ref())?) })
69 }
70 }
71
72 impl<'a> $ref_struct_name<'a> {
73 pub unsafe fn new_unchecked(value: &'a str) -> Self {
74 debug_assert!(Self::is_valid(value));
75
76 Self { value }
77 }
78
79 pub fn new(value: &'a str) -> Result<Self, &'static str> {
80 Ok(unsafe { Self::new_unchecked(Self::validate(value)?) })
81 }
82 }
83
84 impl $owned_struct_name {
85 pub fn value(&self) -> &str {
86 self.value.as_str()
87 }
88
89 pub fn len(&self) -> usize {
90 self.value.len()
91 }
92
93 pub fn is_empty(&self) -> bool {
94 self.value.is_empty()
95 }
96 }
97
98 impl<'a> $ref_struct_name<'a> {
99 pub fn value(&self) -> &'a str {
100 self.value
101 }
102
103 pub fn len(&self) -> usize {
104 self.value.len()
105 }
106
107 pub fn is_empty(&self) -> bool {
108 self.value.is_empty()
109 }
110 }
111
112 impl $owned_struct_name {
113 pub fn to_ref<'a>(&'a self) -> $ref_struct_name<'a> {
114 unsafe { $ref_struct_name::new_unchecked(self.value.as_str()) }
115 }
116 }
117
118 impl<'a> $ref_struct_name<'a> {
119 pub fn to_owned(&self) -> $owned_struct_name {
120 unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
121 }
122 }
123
124 impl AsRef<str> for $owned_struct_name {
125 fn as_ref(&self) -> &str {
126 self.value.as_ref()
127 }
128 }
129
130 impl<'a> AsRef<str> for $ref_struct_name<'a> {
131 fn as_ref(&self) -> &str {
132 self.value
133 }
134 }
135
136 impl Into<String> for $owned_struct_name {
137 fn into(self) -> String {
138 self.value
139 }
140 }
141
142 impl<'a> Into<String> for $ref_struct_name<'a> {
143 fn into(self) -> String {
144 self.value.into()
145 }
146 }
147
148 impl<'a> Into<$owned_struct_name> for $ref_struct_name<'a> {
149 fn into(self) -> $owned_struct_name {
150 self.to_owned()
151 }
152 }
153
154 impl std::fmt::Display for $owned_struct_name {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 write!(f, "{}", self.value)
157 }
158 }
159
160 impl<'a> std::fmt::Display for $ref_struct_name<'a> {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 write!(f, "{}", self.value)
163 }
164 }
165 };
166}
167
168#[cfg(test)]
169#[allow(dead_code)]
170mod tests {
171 custom_string!(Lower, LowerRef, |s: &str| if !s
172 .as_bytes()
173 .iter()
174 .all(|c| c.is_ascii_lowercase())
175 {
176 Err("not lowercase")
177 } else {
178 Ok(())
179 });
180
181 #[test]
182 fn equals() {
183 let one: Lower = Lower::new("one").unwrap();
184 let two: Lower = Lower::new("two").unwrap();
185
186 assert_eq!(one, "one");
187 assert_eq!(one, one);
188 assert_ne!(one, "two");
189 assert_ne!(one, two);
190 }
191
192 #[test]
193 fn validation() {
194 assert!(Lower::is_valid("one"));
195 assert!(!Lower::is_valid("ONE"));
196
197 assert!(Lower::validate("one").is_ok());
198 assert_eq!(Lower::validate("ONE").err().unwrap(), "not lowercase");
199 }
200}