1#[macro_export]
2macro_rules! custom_string {
3 (
4 $(#[$meta:meta])*,
5 $owned_struct_name:ident,
6 $ref_struct_name:ident,
7 $validate_fn:expr
8 ) => {
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 $ref_struct_name<'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 $ref_struct_name<'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, &'static str> {
42 match $validate_fn(value) {
43 Ok(()) => Ok(value),
44 Err(e) => Err(e),
45 }
46 }
47
48 pub fn is_valid(value: &str) -> bool {
50 Self::validate(value).is_ok()
51 }
52 }
53
54 impl<'a> $ref_struct_name<'a> {
55 pub fn validate(value: &str) -> Result<&str, &'static str> {
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, &'static str>
91 where
92 S: AsRef<str> + Into<String>,
93 {
94 Ok(unsafe { Self::new_unchecked(Self::validate(value.as_ref())?) })
95 }
96 }
97
98 impl<'a> $ref_struct_name<'a> {
99 #[doc = concat!("Creates a new `", stringify!($ref_struct_name), "` from the `value`.")]
102 #[doc = ""]
103 #[doc = "# Safety"]
104 #[doc = "The `value` must be valid."]
105 pub unsafe fn new_unchecked(value: &'a str) -> Self {
106 debug_assert!(Self::is_valid(value));
107
108 Self { value }
109 }
110
111 #[doc = concat!("Creates a new `", stringify!($ref_struct_name), "` from the `value`.")]
112 pub fn new(value: &'a str) -> Result<Self, &'static str> {
113 Ok(unsafe { Self::new_unchecked(Self::validate(value)?) })
114 }
115 }
116
117 impl $owned_struct_name {
118 pub fn value(&self) -> &str {
122 self.value.as_str()
123 }
124
125 pub fn len(&self) -> usize {
127 self.value.len()
128 }
129
130 pub fn is_empty(&self) -> bool {
132 self.value.is_empty()
133 }
134 }
135
136 impl<'a> $ref_struct_name<'a> {
137 pub fn value(&self) -> &str {
141 self.value
142 }
143
144 pub fn len(&self) -> usize {
146 self.value.len()
147 }
148
149 pub fn is_empty(&self) -> bool {
151 self.value.is_empty()
152 }
153 }
154
155 impl $owned_struct_name {
156 pub fn to_ref<'a>(&'a self) -> $ref_struct_name<'a> {
160 unsafe { $ref_struct_name::new_unchecked(self.value.as_str()) }
161 }
162 }
163
164 impl<'a> $ref_struct_name<'a> {
165 pub fn to_owned(self) -> $owned_struct_name {
169 unsafe { $owned_struct_name::new_unchecked(self.value.to_string()) }
170 }
171 }
172
173 impl<'a> From<$ref_struct_name<'a>> for $owned_struct_name {
174 fn from(reference: $ref_struct_name<'a>) -> Self {
175 reference.to_owned()
176 }
177 }
178
179 impl From<$owned_struct_name> for String {
180 fn from(value: $owned_struct_name) -> Self {
181 value.value
182 }
183 }
184
185 impl<'a> From<$ref_struct_name<'a>> for String {
186 fn from(value: $ref_struct_name<'a>) -> Self {
187 value.to_string()
188 }
189 }
190
191 impl AsRef<str> for $owned_struct_name {
192 fn as_ref(&self) -> &str {
193 self.value.as_ref()
194 }
195 }
196
197 impl<'a> AsRef<str> for $ref_struct_name<'a> {
198 fn as_ref(&self) -> &str {
199 self.value
200 }
201 }
202
203 impl std::borrow::Borrow<str> for $owned_struct_name {
204 fn borrow(&self) -> &str {
205 self.value.borrow()
206 }
207 }
208
209 impl<'a> std::borrow::Borrow<str> for $ref_struct_name<'a> {
210 fn borrow(&self) -> &str {
211 self.value
212 }
213 }
214
215 impl std::fmt::Display for $owned_struct_name {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 write!(f, "{}", self.value)
218 }
219 }
220
221 impl<'a> std::fmt::Display for $ref_struct_name<'a> {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 write!(f, "{}", self.value)
224 }
225 }
226 };
227}
228
229#[cfg(test)]
230#[allow(dead_code)]
231mod tests {
232 custom_string!(#[doc = "A lowercase string."], Lower, LowerRef, |s: &str| if !s
233 .as_bytes()
234 .iter()
235 .all(|c| c.is_ascii_lowercase())
236 {
237 Err("not lowercase")
238 } else {
239 Ok(())
240 });
241
242 #[test]
243 fn equals() {
244 let one: Lower = Lower::new("one").unwrap();
245 let two: Lower = Lower::new("two").unwrap();
246
247 assert_eq!(one, "one");
248 assert_eq!(one, one);
249 assert_ne!(one, "two");
250 assert_ne!(one, two);
251 }
252
253 #[test]
254 fn validation() {
255 assert!(Lower::is_valid("one"));
256 assert!(!Lower::is_valid("ONE"));
257
258 assert!(Lower::validate("one").is_ok());
259 assert_eq!(Lower::validate("ONE").err().unwrap(), "not lowercase");
260 }
261}