#[macro_export]
macro_rules! wrap {
(#[derive($($derive:ident),*)] $wid:ident<$tid:ident: $bound:path>: $new:item $($item:item)*) => {
#[derive($($derive),*)]
#[doc = concat!(
"See [`",
stringify!($wid),
"::new`].",
)]
pub struct $wid<$tid: $bound>($tid);
impl<$tid: $bound> $wid<$tid> {
$new
$($item)*
#[doc = concat!(
"Construct a `",
stringify!($wid),
"<T>` without checking that the inner value is valid. ",
"If it is not, it may result in undefined behaviour.",
)]
#[allow(dead_code)]
pub fn new_unchecked(inner: $tid) -> Self {
if cfg!(debug_assertions) {
Self::new(inner).unwrap()
} else {
Self(inner)
}
}
#[allow(dead_code)]
pub fn unwrap(self) -> $tid {
self.0
}
#[doc = concat!(
"Map a `",
stringify!($wid),
"<T>` to a `",
stringify!($wid),
"<U>` by applying a function to the wrapped value. ",
"It does not check that the value returned by the function is valid. ",
"If it is not, it may result in undefined behaviour.",
)]
#[allow(dead_code)]
pub fn map_unchecked<F, U>(self, f: F) -> $wid<U>
where
F: FnOnce(T) -> U,
U: $bound,
{
let inner = self.unwrap();
let new_inner: U = f(inner);
$wid(new_inner)
}
}
impl<T: $bound> std::ops::Deref for $wid<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: $bound> std::convert::AsRef<T> for $wid<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: $bound> std::borrow::Borrow<T> for $wid<T> {
fn borrow(&self) -> &T {
&self.0
}
}
impl<T, U> std::cmp::PartialEq<$wid<T>> for $wid<U>
where
T: $bound,
U: $bound + std::cmp::PartialEq<T>,
{
fn eq(&self, rhs: &$wid<T>) -> bool {
self.0 == rhs.0
}
}
impl<T> std::cmp::Eq for $wid<T>
where
T: $bound + std::cmp::Eq,
{}
impl<T, U> std::cmp::PartialOrd<$wid<T>> for $wid<U>
where
T: $bound,
U: $bound + std::cmp::PartialOrd<T>,
{
fn partial_cmp(&self, rhs: &$wid<T>) -> std::option::Option<std::cmp::Ordering> {
std::cmp::PartialOrd::partial_cmp(&self.0, &rhs.0)
}
}
impl<T> std::cmp::Ord for $wid<T>
where
T: $bound + std::cmp::Eq + std::cmp::Ord,
{
fn cmp(&self, rhs: &$wid<T>) -> std::cmp::Ordering {
std::cmp::Ord::cmp(&self.0, &rhs.0)
}
}
impl<T> std::hash::Hash for $wid<T>
where
T: $bound + std::hash::Hash,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
};
($wid:ident<$tid:ident: $bound:path>: $new:item $($item:item)*) => {
$crate::wrap!( #[derive(Clone, Copy, Debug)] $wid<$tid: $bound>: $new $($item)* );
};
($wid:ident borrowing str: $new:item $($item:item)*) => {
$crate::wrap!{
#[derive(Clone, Copy)]
$wid borrowing str:
$new
$($item)*
pub fn as_str(&self) -> &str {
self.0.borrow()
}
}
impl<T: std::borrow::Borrow<str>> std::fmt::Debug for $wid<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
write!(f, "{}({:?})", stringify!($wid), self.0.borrow())
}
}
};
(#[derive($($derive:ident),*)] $wid:ident borrowing $bid:ty: $new:item $($item:item)*) => {
$crate::wrap!( #[derive($($derive),*)] $wid<T: std::borrow::Borrow<$bid>>: $new $($item)* );
impl<T> $wid<T>
where
T: std::borrow::Borrow<$bid>,
{
#[doc = concat!(
"Convert from `&",
stringify!($wid),
"<T>` to `",
stringify!($wid),
"<&",
stringify!($bid),
">`.",
)]
#[allow(dead_code)]
pub fn as_ref(&self) -> $wid<&$bid> {
$wid(self.0.borrow())
}
}
impl $wid<&'static $bid> {
#[doc = concat!(
"Construct a `",
stringify!($wid),
"<&'static ",
stringify!($bid),
">` without checking that the inner value is valid. ",
"If it is not, it may result in undefined behaviour.",
)]
#[allow(dead_code)]
#[must_use] pub const fn new_unchecked_const(inner: &'static $bid) -> Self {
$wid(inner)
}
}
impl<T: std::borrow::Borrow<$bid>> std::convert::AsRef<$bid> for $wid<T> {
fn as_ref(&self) -> &$bid {
&self.0.borrow()
}
}
impl<T: std::borrow::Borrow<$bid>> std::borrow::Borrow<$bid> for $wid<T> {
fn borrow(&self) -> &$bid {
&self.0.borrow()
}
}
impl<T> std::cmp::PartialEq<$bid> for $wid<T>
where
T: std::borrow::Borrow<$bid>,
$bid: std::cmp::PartialEq,
{
fn eq(&self, other: &$bid) -> bool {
self.0.borrow() == other
}
}
impl<T> std::cmp::PartialEq<$wid<T>> for $bid
where
T: std::borrow::Borrow<$bid>,
$bid: std::cmp::PartialEq,
{
fn eq(&self, other: &$wid<T>) -> bool {
self == other.0.borrow()
}
}
impl<T> std::cmp::PartialOrd<$bid> for $wid<T>
where
T: std::borrow::Borrow<$bid>,
$bid: std::cmp::PartialOrd,
{
fn partial_cmp(&self, other: &$bid) -> std::option::Option<std::cmp::Ordering> {
self.0.borrow().partial_cmp(other)
}
}
impl<T> std::cmp::PartialOrd<$wid<T>> for $bid
where
T: std::borrow::Borrow<$bid>,
$bid: std::cmp::PartialOrd,
{
fn partial_cmp(&self, other: &$wid<T>) -> std::option::Option<std::cmp::Ordering> {
self.partial_cmp(other.0.borrow())
}
}
};
($wid:ident borrowing $bid:ty: $new:item $($item:item)*) => {
$crate::wrap!( #[derive(Clone, Copy, Debug)] $wid borrowing $bid: $new $($item)* );
};
}
#[cfg(test)]
pub mod test_simple_wrap {
pub trait Number {
fn even(&self) -> bool;
}
impl Number for i32 {
fn even(&self) -> bool {
*self % 2 == 0
}
}
impl Number for isize {
fn even(&self) -> bool {
*self % 2 == 0
}
}
wrap! { Even<T: Number>:
pub fn new(inner: T) -> Result<Self, ()> {
if inner.even() {
Ok(Even(inner))
} else {
Err(())
}
}
}
#[test]
fn constructor_succeeds() {
assert!(Even::new(42).is_ok());
}
#[test]
fn constructor_fails() {
assert!(Even::new(43).is_err());
}
#[allow(dead_code)]
fn unwrap() {
let even = Even(42);
let _: isize = even.unwrap();
}
#[allow(dead_code)]
fn deref() {
let even = Even(42);
let _: &isize = &even;
}
#[allow(dead_code)]
fn as_ref() {
let even = Even(42);
let _: &isize = even.as_ref();
}
#[allow(dead_code)]
fn borrow() {
use std::borrow::Borrow;
let even = Even(42);
let _: &isize = even.borrow();
}
}
#[cfg(test)]
pub mod test_wrap_borrowing {
wrap! { Foo borrowing str :
pub fn new(inner: T) -> Result<Self, ()> {
if inner.borrow().contains("foo") {
Ok(Foo(inner))
} else {
Err(())
}
}
}
#[test]
fn new_succeeds() {
assert!(Foo::new("this foo is good").is_ok());
}
#[test]
fn new_fails() {
assert!(Foo::new("this bar is bad").is_err());
}
#[test]
fn partial_eq() {
let f1a = Foo::new("foo1".to_string()).unwrap();
let f1b = Foo::new("foo1").unwrap();
let f2 = Foo::new("foo2").unwrap();
assert_eq!(&f1a, "foo1");
assert_eq!(&f1a, "foo1");
assert_eq!(&f2, "foo2");
assert_ne!(&f2, "foo1");
assert_eq!("foo1", &f1a);
assert_eq!("foo1", &f1a);
assert_eq!("foo2", &f2);
assert_ne!("foo1", &f2);
assert_eq!(f1a.as_str(), f1b.as_str());
}
#[allow(dead_code)]
fn new_unchecked() {
let _: Foo<String> = Foo::new_unchecked(String::new());
}
#[allow(dead_code)]
fn unwrap() {
let foo = Foo("foo".to_string());
let _: String = foo.unwrap();
}
#[allow(dead_code)]
fn deref() {
let foo = Foo("this foo is good".to_string());
let _: &String = &foo;
let _: &str = &foo;
}
#[allow(dead_code)]
fn as_ref_trait() {
let foo = Foo("this foo is good".to_string());
let _: &String = AsRef::as_ref(&foo);
let _: &str = AsRef::as_ref(&foo);
}
#[allow(dead_code)]
fn borrow() {
use std::borrow::Borrow;
let foo = Foo("this foo is good".to_string());
let _: &String = foo.borrow();
let _: &str = foo.borrow();
}
#[allow(dead_code)]
fn as_ref() {
let foo = Foo("this foo is good".to_string());
let _: Foo<&str> = foo.as_ref();
}
}