sophia_iri/
_wrap_macro.rs1#[macro_export]
2macro_rules! wrap {
57 ($wid:ident<$tid:ident: $bound:path>: $new:item $($item:item)*) => {
58 #[derive(Clone, Copy, Debug)]
59
60 #[doc = concat!(
61 "See [`",
62 stringify!($wid),
63 "::new`].",
64 )]
65 pub struct $wid<$tid: $bound>($tid);
66
67 impl<$tid: $bound> $wid<$tid> {
68 $new
69 $($item)*
70
71 #[doc = concat!(
72 "Construct a `",
73 stringify!($wid),
74 "<T>` without checking that the inner value is valid. ",
75 "If it is not, it may result in undefined behaviour.",
76 )]
77 #[allow(dead_code)]
78 pub fn new_unchecked(inner: $tid) -> Self {
79 if cfg!(debug_assertions) {
80 Self::new(inner).unwrap()
81 } else {
82 Self(inner)
83 }
84 }
85
86 #[allow(dead_code)]
88 pub fn unwrap(self) -> $tid {
89 self.0
90 }
91
92 #[doc = concat!(
93 "Map a `",
94 stringify!($wid),
95 "<T>` to a `",
96 stringify!($wid),
97 "<U>` by applying a function to the wrapped value. ",
98 "It does not check that the value returned by the function is valid. ",
99 "If it is not, it may result in undefined behaviour.",
100 )]
101 #[allow(dead_code)]
102 pub fn map_unchecked<F, U>(self, f: F) -> $wid<U>
103 where
104 F: FnOnce(T) -> U,
105 U: $bound,
106 {
107 let inner = self.unwrap();
108 let new_inner: U = f(inner);
109 $wid(new_inner)
110 }
111 }
112
113 impl<T: $bound> std::ops::Deref for $wid<T> {
114 type Target = T;
115 fn deref(&self) -> &T {
116 &self.0
117 }
118 }
119
120 impl<T: $bound> std::convert::AsRef<T> for $wid<T> {
121 fn as_ref(&self) -> &T {
122 &self.0
123 }
124 }
125
126 impl<T: $bound> std::borrow::Borrow<T> for $wid<T> {
127 fn borrow(&self) -> &T {
128 &self.0
129 }
130 }
131
132 impl<T, U> std::cmp::PartialEq<$wid<T>> for $wid<U>
133 where
134 T: $bound,
135 U: $bound + std::cmp::PartialEq<T>,
136 {
137 fn eq(&self, rhs: &$wid<T>) -> bool {
138 self.0 == rhs.0
139 }
140 }
141
142 impl<T> std::cmp::Eq for $wid<T>
143 where
144 T: $bound + std::cmp::Eq,
145 {}
146
147 impl<T, U> std::cmp::PartialOrd<$wid<T>> for $wid<U>
148 where
149 T: $bound,
150 U: $bound + std::cmp::PartialOrd<T>,
151 {
152 fn partial_cmp(&self, rhs: &$wid<T>) -> std::option::Option<std::cmp::Ordering> {
153 std::cmp::PartialOrd::partial_cmp(&self.0, &rhs.0)
154 }
155 }
156
157 impl<T> std::cmp::Ord for $wid<T>
158 where
159 T: $bound + std::cmp::Eq + std::cmp::Ord,
160 {
161 fn cmp(&self, rhs: &$wid<T>) -> std::cmp::Ordering {
162 std::cmp::Ord::cmp(&self.0, &rhs.0)
163 }
164 }
165
166
167 impl<T> std::hash::Hash for $wid<T>
168 where
169 T: $bound + std::hash::Hash,
170 {
171 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
172 self.0.hash(state);
173 }
174 }
175 };
176 ($wid:ident borrowing $bid:ty: $new:item $($item:item)*) => {
177 $crate::wrap!($wid<T: std::borrow::Borrow<$bid>>: $new $($item)*);
178
179 impl<T> $wid<T>
180 where
181 T: std::borrow::Borrow<$bid>,
182 {
183 #[doc = concat!(
184 "Convert from `&",
185 stringify!($wid),
186 "<T>` to `",
187 stringify!($wid),
188 "<&",
189 stringify!($bid),
190 ">`.",
191 )]
192 #[allow(dead_code)]
193 pub fn as_ref(&self) -> $wid<&$bid> {
194 $wid(self.0.borrow())
195 }
196 }
197
198 impl $wid<&'static $bid> {
199 #[doc = concat!(
200 "Construct a `",
201 stringify!($wid),
202 "<&'static ",
203 stringify!($bid),
204 ">` without checking that the inner value is valid. ",
205 "If it is not, it may result in undefined behaviour.",
206 )]
207 #[allow(dead_code)]
208 pub const fn new_unchecked_const(inner: &'static $bid) -> Self {
209 $wid(inner)
210 }
211 }
212
213 impl<T: std::borrow::Borrow<$bid>> std::convert::AsRef<$bid> for $wid<T> {
214 fn as_ref(&self) -> &$bid {
215 &self.0.borrow()
216 }
217 }
218
219 impl<T: std::borrow::Borrow<$bid>> std::borrow::Borrow<$bid> for $wid<T> {
220 fn borrow(&self) -> &$bid {
221 &self.0.borrow()
222 }
223 }
224
225 impl<T> std::cmp::PartialEq<$bid> for $wid<T>
226 where
227 T: std::borrow::Borrow<$bid>,
228 $bid: std::cmp::PartialEq,
229 {
230 fn eq(&self, other: &$bid) -> bool {
231 self.0.borrow() == other
232 }
233 }
234
235 impl<T> std::cmp::PartialEq<$wid<T>> for $bid
236 where
237 T: std::borrow::Borrow<$bid>,
238 $bid: std::cmp::PartialEq,
239 {
240 fn eq(&self, other: &$wid<T>) -> bool {
241 self == other.0.borrow()
242 }
243 }
244
245 impl<T> std::cmp::PartialOrd<$bid> for $wid<T>
246 where
247 T: std::borrow::Borrow<$bid>,
248 $bid: std::cmp::PartialOrd,
249 {
250 fn partial_cmp(&self, other: &$bid) -> std::option::Option<std::cmp::Ordering> {
251 self.0.borrow().partial_cmp(other)
252 }
253 }
254
255 impl<T> std::cmp::PartialOrd<$wid<T>> for $bid
256 where
257 T: std::borrow::Borrow<$bid>,
258 $bid: std::cmp::PartialOrd,
259 {
260 fn partial_cmp(&self, other: &$wid<T>) -> std::option::Option<std::cmp::Ordering> {
261 self.partial_cmp(other.0.borrow())
262 }
263 }
264 };
265}
266
267#[cfg(test)]
268pub mod test_simple_wrap {
269 pub trait Number {
270 fn even(&self) -> bool;
271 }
272 impl Number for i32 {
273 fn even(&self) -> bool {
274 *self % 2 == 0
275 }
276 }
277 impl Number for isize {
278 fn even(&self) -> bool {
279 *self % 2 == 0
280 }
281 }
282
283 wrap! { Even<T: Number>:
284 pub fn new(inner: T) -> Result<Self, ()> {
285 if inner.even() {
286 Ok(Even(inner))
287 } else {
288 Err(())
289 }
290 }
291 }
292
293 #[test]
294 fn constructor_succeeds() {
295 assert!(Even::new(42).is_ok());
296 }
297
298 #[test]
299 fn constructor_fails() {
300 assert!(Even::new(43).is_err());
301 }
302
303 #[allow(dead_code)]
305 fn unwrap() {
306 let even = Even(42);
307 let _: isize = even.unwrap();
308 }
309
310 #[allow(dead_code)]
312 fn deref() {
313 let even = Even(42);
314 let _: &isize = &even;
315 }
316
317 #[allow(dead_code)]
319 fn as_ref() {
320 let even = Even(42);
321 let _: &isize = even.as_ref();
322 }
323
324 #[allow(dead_code)]
326 fn borrow() {
327 use std::borrow::Borrow;
328 let even = Even(42);
329 let _: &isize = even.borrow();
330 }
331}
332
333#[cfg(test)]
334pub mod test_wrap_borrowing {
335 wrap! { Foo borrowing str :
336 pub fn new(inner: T) -> Result<Self, ()> {
338 if inner.borrow().contains("foo") {
339 Ok(Foo(inner))
340 } else {
341 Err(())
342 }
343 }
344
345 pub fn as_str(&self) -> &str {
346 self.0.borrow()
347 }
348 }
349
350 #[test]
351 fn new_succeeds() {
352 assert!(Foo::new("this foo is good").is_ok());
353 }
354
355 #[test]
356 fn new_fails() {
357 assert!(Foo::new("this bar is bad").is_err());
358 }
359
360 #[test]
361 fn partial_eq() {
362 let f1a = Foo::new("foo1".to_string()).unwrap();
363 let f1b = Foo::new("foo1").unwrap();
364 let f2 = Foo::new("foo2").unwrap();
365 assert_eq!(&f1a, "foo1");
366 assert_eq!(&f1a, "foo1");
367 assert_eq!(&f2, "foo2");
368 assert_ne!(&f2, "foo1");
369 assert_eq!("foo1", &f1a);
370 assert_eq!("foo1", &f1a);
371 assert_eq!("foo2", &f2);
372 assert_ne!("foo1", &f2);
373 assert_eq!(f1a.as_str(), f1b.as_str());
374 }
375
376 #[allow(dead_code)]
378 fn new_unchecked() {
379 let _: Foo<String> = Foo::new_unchecked("".into());
380 }
381
382 #[allow(dead_code)]
384 fn unwrap() {
385 let foo = Foo("foo".to_string());
386 let _: String = foo.unwrap();
387 }
388
389 #[allow(dead_code)]
391 fn deref() {
392 let foo = Foo("this foo is good".to_string());
393 let _: &String = &foo;
394 let _: &str = &foo;
395 }
396
397 #[allow(dead_code)]
399 fn as_ref_trait() {
400 let foo = Foo("this foo is good".to_string());
401 let _: &String = AsRef::as_ref(&foo);
402 let _: &str = AsRef::as_ref(&foo);
403 }
404
405 #[allow(dead_code)]
407 fn borrow() {
408 use std::borrow::Borrow;
409 let foo = Foo("this foo is good".to_string());
410 let _: &String = foo.borrow();
411 let _: &str = foo.borrow();
412 }
413
414 #[allow(dead_code)]
416 fn as_ref() {
417 let foo = Foo("this foo is good".to_string());
418 let _: Foo<&str> = foo.as_ref();
419 }
420}