sophia_iri/
_wrap_macro.rs

1#[macro_export]
2/// This macro is used to create a read-only wrapper around a type T,
3/// usually for the purpose of guaranteeing that the wrapped value verifies some condition.
4/// This macro takes care of defining all the usual traits for the wrapper type.
5///
6/// In its most general form, it is used like this:
7/// ```
8/// # #[macro_use] extern crate sophia_iri;
9/// # trait SomeTrait { fn check_some_property(&self) -> bool; }
10/// # #[derive(Debug)]
11/// # struct SomeError {}
12/// wrap! { MyWrapper<T: SomeTrait> :
13///     // NB: the trait bound is required, as well as the trailing colon (':').
14///
15///     // You can include members in the impl of the wrapper type.
16///     // At the very least, you must define a `new` constructor,
17///     // that will check the appropriate condition on the wrapped value,
18///     // and return a `Result`.
19///
20///     /// The `new` constructor should carry the documentation of the type;
21///     /// since the generated documentation for the type will point to it.
22///     pub fn new(inner: T) -> Result<Self, SomeError> {
23///         if inner.check_some_property() {
24///             Ok(Self(inner))
25///         } else {
26///             Err(SomeError {})
27///         }
28///     }
29/// }
30/// ```
31///
32/// A more specific form is when the trait that T must satisfy
33/// is [`std::borrow::Borrow<U>`], in which case more traits
34/// can be implemented by the wrapper. This is achieved with
35/// the following syntax:
36/// ```
37/// # #[macro_use] extern crate sophia_iri;
38/// # #[derive(Debug)]
39/// # struct SomeError {}
40/// wrap! { Foo borrowing str :
41///     /// As before
42///     pub fn new(inner: T) -> Result<Self, String> {
43///         if inner.borrow().contains("foo") {
44///             Ok(Self(inner))
45///         } else {
46///             Err(format!("{:?} is not a valid Foo", inner.borrow()))
47///         }
48///     }
49/// }
50/// ```
51///
52/// Two [examples](crate::wrap_macro_examples) are availble,
53/// illustrating the members and trait implementation generated by this macro.
54///
55/// NB: the documentation of the wrapper will point to the documentation of the `new` method.
56macro_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            /// Returns the wrapped value, consuming `self`.
87            #[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    // only check that this compiles
304    #[allow(dead_code)]
305    fn unwrap() {
306        let even = Even(42);
307        let _: isize = even.unwrap();
308    }
309
310    // only check that this compiles
311    #[allow(dead_code)]
312    fn deref() {
313        let even = Even(42);
314        let _: &isize = &even;
315    }
316
317    // only check that this compiles
318    #[allow(dead_code)]
319    fn as_ref() {
320        let even = Even(42);
321        let _: &isize = even.as_ref();
322    }
323
324    // only check that this compiles
325    #[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        /// The constructor of Foo
337        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    // only check that this compiles
377    #[allow(dead_code)]
378    fn new_unchecked() {
379        let _: Foo<String> = Foo::new_unchecked("".into());
380    }
381
382    // only check that this compiles
383    #[allow(dead_code)]
384    fn unwrap() {
385        let foo = Foo("foo".to_string());
386        let _: String = foo.unwrap();
387    }
388
389    // only check that this compiles
390    #[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    // only check that this compiles
398    #[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    // only check that this compiles
406    #[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    // only check that this compiles
415    #[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}