macro_rules! import_std_or_alloc {
($($path:ident)::* :: {$($id:ident $(as $alias:ident)?),* $(,)?}) => {
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::$($path)::*::{$($id $(as $alias)?),*};
#[cfg(feature = "std")]
use std::$($path)::*::{$($id $(as $alias)?),*};
};
}
use crate::{Error, Optionable, OptionableConvert};
use core::cell::{Cell, RefCell};
#[cfg(any(feature = "alloc", feature = "std"))]
import_std_or_alloc!(boxed::{Box});
import_std_or_alloc!(borrow::{Cow, ToOwned});
import_std_or_alloc!(vec::{Vec});
import_std_or_alloc!(collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque});
#[cfg(feature = "std")]
use std::collections::{HashMap, HashSet};
import_std_or_alloc!(string::{String});
#[cfg(feature = "std")]
use std::ffi::{OsStr, OsString};
#[cfg(feature = "std")]
use std::hash::{BuildHasher, Hash};
#[cfg(feature = "std")]
use std::path::{Path, PathBuf};
import_std_or_alloc!(rc::{Rc, Weak as RcWeak});
import_std_or_alloc!(sync::{Arc, Weak as ArcWeak});
use core::mem::MaybeUninit;
#[cfg(feature = "std")]
use std::sync::{Mutex, RwLock};
#[cfg(feature = "std")]
use std::time::Duration;
impl<'a, T: ?Sized + Optionable> Optionable for &'a T {
type Optioned = &'a T::Optioned;
}
impl<'a, T: ?Sized + Optionable> Optionable for &'a mut T {
type Optioned = &'a mut T::Optioned;
}
macro_rules! impl_optional_self {
($($(#[$attr:meta])? $t:ty),+ $(,)?) => {
$(
$(#[$attr])?
impl crate::Optionable for $t{
type Optioned = Self;
}
$(#[$attr])?
impl crate::OptionableConvert for $t{
fn into_optioned(self) -> $t {
self
}
fn try_from_optioned(value: Self::Optioned) -> Result<Self, crate::Error> {
Ok(value)
}
fn merge(&mut self, other: Self::Optioned) -> Result<(), crate::Error> {
*self = other;
Ok(())
}
}
$(#[$attr])?
impl crate::OptionedConvert<$t> for $t{
fn from_optionable(value: $t) -> Self {
value
}
fn try_into_optionable(self) -> Result<$t, crate::Error> {
Ok(self)
}
fn merge_into(self, other: &mut $t) -> Result<(), crate::Error>{
*other = self;
Ok(())
}
}
)*
};
}
macro_rules! impl_optioned_from_optionable {
($t:ty) => {
fn from_optionable(value: $t) -> Self {
OptionableConvert::into_optioned(value)
}
fn try_into_optionable(self) -> Result<$t, Error> {
OptionableConvert::try_from_optioned(self)
}
fn merge_into(self, other: &mut $t) -> Result<(), Error> {
OptionableConvert::merge(other, self)
}
};
}
#[cfg(any(feature = "chrono04", feature = "jiff02", feature = "serde_json"))]
pub(crate) use impl_optional_self;
macro_rules! impl_optional_self_unsized {
($($(#[$attr:meta])? $t:ty),+ $(,)?) => {
$(
$(#[$attr])?
impl crate::Optionable for $t{
type Optioned = Self;
})*
}
}
impl_optional_self!(
i8,
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize,
f32,
f64,
char,
bool,
(),
#[cfg(any(feature = "alloc", feature = "std"))]
String,
#[cfg(feature = "std")]
OsString,
#[cfg(feature = "std")]
Duration,
#[cfg(feature = "std")]
PathBuf
);
impl_optional_self_unsized!(
str,
#[cfg(feature = "std")]
OsStr,
#[cfg(feature = "std")]
Path
);
macro_rules! impl_container{
($($(#[$attr:meta])? $t:ident),* $(,)?) => {
$(
$(#[$attr])?
impl<T: Optionable> Optionable for $t<T>
where T::Optioned: Sized
{
type Optioned = $t<T::Optioned>;
}
)*
};
}
macro_rules! impl_container_unsized {
($($(#[$attr:meta])? $t:ident),* $(,)?) => {
$(
$(#[$attr])?
impl<T: Optionable> Optionable for $t<T>
{
type Optioned = $t<T::Optioned>;
}
)*
};
}
#[cfg(any(feature = "alloc", feature = "std"))]
macro_rules! inner_impl_convert_into_iter {
($t:ty) => {
fn into_optioned(self) -> <$t as Optionable>::Optioned {
self.into_iter().map(T::into_optioned).collect()
}
fn try_from_optioned(value: <$t as Optionable>::Optioned) -> Result<Self, Error> {
value.into_iter().map(T::try_from_optioned).collect()
}
fn merge(&mut self, other: <$t as Optionable>::Optioned) -> Result<(), Error> {
*self = Self::try_from_optioned(other)?;
Ok(())
}
};
}
#[cfg(any(feature = "alloc", feature = "std"))]
macro_rules! impl_container_convert_linear {
($($t:ident),+ $(, where=$w:ident)?) => {
$(
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> OptionableConvert for $t<T>
where T::Optioned: Sized{
inner_impl_convert_into_iter!($t<T>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> crate::OptionedConvert<$t<T>> for <$t<T> as Optionable>::Optioned
where T::Optioned: Sized{
impl_optioned_from_optionable!($t<T>);
}
)*
};
}
#[cfg(any(feature = "alloc", feature = "std"))]
macro_rules! impl_container_convert_linear_ord {
($($t:ident),+ $(, where=$w:ident)?) => {
$(
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> OptionableConvert for $t<T>
where T: Ord,
T::Optioned: Sized+Ord{
inner_impl_convert_into_iter!($t<T>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> crate::OptionedConvert<$t<T>> for <$t<T> as Optionable>::Optioned
where T: Ord,
T::Optioned: Sized+Ord{
impl_optioned_from_optionable!($t<T>);
}
)*
};
}
impl_container!(
Option,
#[cfg(any(feature = "alloc", feature = "std"))]
Vec,
#[cfg(any(feature = "alloc", feature = "std"))]
VecDeque,
#[cfg(any(feature = "alloc", feature = "std"))]
LinkedList,
#[cfg(any(feature = "alloc", feature = "std"))]
BTreeSet,
#[cfg(any(feature = "alloc", feature = "std"))]
BinaryHeap
);
impl_container_unsized!(
Cell,
RefCell,
#[cfg(any(feature = "alloc", feature = "std"))]
Box,
#[cfg(any(feature = "alloc", feature = "std"))]
Rc,
#[cfg(any(feature = "alloc", feature = "std"))]
RcWeak,
#[cfg(any(feature = "alloc", feature = "std"))]
Arc,
#[cfg(any(feature = "alloc", feature = "std"))]
ArcWeak,
#[cfg(feature = "std")]
Mutex,
#[cfg(feature = "std")]
RwLock,
);
#[cfg(any(feature = "alloc", feature = "std"))]
impl_container_convert_linear!(Vec, VecDeque, LinkedList);
#[cfg(any(feature = "alloc", feature = "std"))]
impl_container_convert_linear_ord!(BTreeSet, BinaryHeap);
impl<T: Optionable, const N: usize> Optionable for [T; N]
where
T::Optioned: Sized,
{
type Optioned = [T::Optioned; N];
}
impl<T: OptionableConvert, const N: usize> OptionableConvert for [T; N]
where
T::Optioned: Sized,
{
fn into_optioned(self) -> Self::Optioned {
self.map(T::into_optioned)
}
fn try_from_optioned(value: Self::Optioned) -> Result<Self, Error> {
let mut result = MaybeUninit::<[T; N]>::uninit();
unsafe {
let result = result.as_mut_ptr();
for (i, el) in value.into_iter().enumerate() {
(*result)[i] = T::try_from_optioned(el)?;
}
}
unsafe {
Ok(result.assume_init())
}
}
fn merge(&mut self, other: Self::Optioned) -> Result<(), Error> {
*self = Self::try_from_optioned(other)?;
Ok(())
}
}
impl<T: OptionableConvert, const N: usize> crate::OptionedConvert<[T; N]> for [T::Optioned; N]
where
T::Optioned: Sized,
{
impl_optioned_from_optionable!([T; N]);
}
impl<T: Optionable> Optionable for [T]
where
T::Optioned: Sized,
{
type Optioned = [T::Optioned];
}
impl<T: OptionableConvert> OptionableConvert for Option<T>
where
T::Optioned: Sized,
{
fn into_optioned(self) -> Option<T::Optioned> {
self.map(T::into_optioned)
}
fn try_from_optioned(value: Option<T::Optioned>) -> Result<Self, Error> {
value.map(T::try_from_optioned).transpose()
}
fn merge(&mut self, other: Option<T::Optioned>) -> Result<(), Error> {
if let Some(val) = self {
if let Some(val_other) = other {
val.merge(val_other)?;
}
} else if let Some(val_other) = other {
*self = Some(T::try_from_optioned(val_other)?);
}
Ok(())
}
}
impl<T: OptionableConvert> crate::OptionedConvert<Option<T>> for Option<T::Optioned>
where
T::Optioned: Sized,
{
impl_optioned_from_optionable!(Option<T>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<'a, T: ?Sized + Optionable + ToOwned> Optionable for Cow<'a, T>
where
T::Optioned: ToOwned,
{
type Optioned = Cow<'a, T::Optioned>;
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert + Clone> OptionableConvert for Cow<'_, T>
where
T::Optioned: Clone,
{
fn into_optioned(self) -> Self::Optioned {
Cow::Owned(self.into_owned().into_optioned())
}
fn try_from_optioned(value: Self::Optioned) -> Result<Self, Error> {
T::try_from_optioned(value.into_owned()).map(Cow::Owned)
}
fn merge(&mut self, other: Self::Optioned) -> Result<(), Error> {
self.to_mut().merge(other.into_owned())
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<'a, T: OptionableConvert + Clone> crate::OptionedConvert<Cow<'a, T>> for Cow<'a, T::Optioned>
where
T::Optioned: Clone,
{
impl_optioned_from_optionable!(Cow<'a, T>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> OptionableConvert for Box<T>
where
T::Optioned: Sized,
{
fn into_optioned(self) -> Box<T::Optioned> {
let inner = *self;
Box::new(T::into_optioned(inner))
}
fn try_from_optioned(value: Box<T::Optioned>) -> Result<Self, Error> {
let inner = *value;
Ok(Box::new(T::try_from_optioned(inner)?))
}
fn merge(&mut self, other: Box<T::Optioned>) -> Result<(), Error> {
let inner = &mut **self;
let other_inner = *other;
inner.merge(other_inner)?;
Ok(())
}
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<T: OptionableConvert> crate::OptionedConvert<Box<T>> for Box<T::Optioned>
where
T::Optioned: Sized,
{
impl_optioned_from_optionable!(Box<T>);
}
impl<T: Optionable, E> Optionable for Result<T, E>
where
T::Optioned: Sized,
{
type Optioned = Result<T::Optioned, E>;
}
#[cfg(feature = "std")]
impl<T: Optionable, S> Optionable for HashSet<T, S>
where
T::Optioned: Sized,
{
type Optioned = HashSet<T::Optioned, S>;
}
#[cfg(feature = "std")]
impl<T: OptionableConvert, S: Default + BuildHasher> OptionableConvert for HashSet<T, S>
where
T: Ord + Hash,
T::Optioned: Sized + Eq + Hash,
{
inner_impl_convert_into_iter!(HashSet<T,S>);
}
#[cfg(feature = "std")]
impl<T: OptionableConvert, S: Default + BuildHasher> crate::OptionedConvert<HashSet<T, S>>
for HashSet<T::Optioned, S>
where
T: Ord + Hash,
T::Optioned: Sized + Eq + Hash,
{
impl_optioned_from_optionable!(HashSet<T, S>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<K, T: Optionable> Optionable for BTreeMap<K, T>
where
T::Optioned: Sized,
{
type Optioned = BTreeMap<K, T::Optioned>;
}
#[cfg(any(feature = "alloc", feature = "std"))]
macro_rules! inner_impl_convert_map {
($t:ty) => {
fn into_optioned(self) -> $t {
self.into_iter()
.map(|(k, v)| (k, T::into_optioned(v)))
.collect()
}
fn try_from_optioned(value: $t) -> Result<Self, Error> {
value
.into_iter()
.map(|(k, v)| Ok((k, T::try_from_optioned(v)?)))
.collect()
}
fn merge(&mut self, other: $t) -> Result<(), Error> {
other.into_iter().try_for_each(|(k, v)| {
if let Some(self_el) = self.get_mut(&k) {
self_el.merge(v)?;
} else {
self.insert(k, T::try_from_optioned(v)?);
}
Ok(())
})
}
};
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<K: Ord, T: OptionableConvert> OptionableConvert for BTreeMap<K, T>
where
T::Optioned: Sized,
{
inner_impl_convert_map!(BTreeMap<K, T::Optioned>);
}
#[cfg(any(feature = "alloc", feature = "std"))]
impl<K: Ord, T: OptionableConvert> crate::OptionedConvert<BTreeMap<K, T>>
for BTreeMap<K, T::Optioned>
where
T::Optioned: Sized,
{
impl_optioned_from_optionable!(BTreeMap<K, T>);
}
#[cfg(feature = "std")]
impl<K, T: Optionable, S> Optionable for HashMap<K, T, S>
where
T::Optioned: Sized,
{
type Optioned = HashMap<K, T::Optioned, S>;
}
#[cfg(feature = "std")]
impl<K: Ord + Hash, T: OptionableConvert, S: BuildHasher + Default> OptionableConvert
for HashMap<K, T, S>
where
T::Optioned: Sized,
{
inner_impl_convert_map!(HashMap<K, T::Optioned, S>);
}
#[cfg(feature = "std")]
impl<K: Ord + Hash, T: OptionableConvert, S: BuildHasher + Default>
crate::OptionedConvert<HashMap<K, T, S>> for HashMap<K, T::Optioned, S>
where
T::Optioned: Sized,
{
impl_optioned_from_optionable!(HashMap<K, T, S>);
}
macro_rules! impl_tuple {
($(($T:ident, $i:tt)),*) => {
impl<$($T),*> Optionable for ($($T),*)
where
$(
$T: Optionable,
$T::Optioned: Sized
),*
{
type Optioned = ($(Option::<$T::Optioned>),*);
}
impl<$($T),*> OptionableConvert for ($($T),*)
where
$(
$T: OptionableConvert,
$T::Optioned: Sized
),*
{
fn into_optioned(self) -> Self::Optioned {
($(Some(self.$i.into_optioned())),*)
}
fn try_from_optioned(value: Self::Optioned) -> Result<Self, Error> {
Ok((
$($T::try_from_optioned(value.$i.ok_or(Error { missing_field: "$i" })?)?),*
))
}
fn merge(&mut self, other: Self::Optioned) -> Result<(), Error> {
$(if let Some(other_value) = other.$i { self.$i.merge(other_value)?; })*
Ok(())
}
}
impl<$($T),*> crate::OptionedConvert<($($T),*)> for <($($T),*) as Optionable>::Optioned
where
$(
$T: OptionableConvert,
$T::Optioned: Sized
),*
{
impl_optioned_from_optionable!(($($T),*));
}
};
}
impl_tuple!((T0, 0), (T1, 1));
impl_tuple!((T0, 0), (T1, 1), (T2, 2));
impl_tuple!((T0, 0), (T1, 1), (T2, 2), (T3, 3));
impl_tuple!((T0, 0), (T1, 1), (T2, 2), (T3, 3), (T4, 4));
impl_tuple!((T0, 0), (T1, 1), (T2, 2), (T3, 3), (T4, 4), (T5, 5));
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10),
(T11, 11)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10),
(T11, 11),
(T12, 12)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10),
(T11, 11),
(T12, 12),
(T13, 13)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10),
(T11, 11),
(T12, 12),
(T13, 13),
(T14, 14)
);
impl_tuple!(
(T0, 0),
(T1, 1),
(T2, 2),
(T3, 3),
(T4, 4),
(T5, 5),
(T6, 6),
(T7, 7),
(T8, 8),
(T9, 9),
(T10, 10),
(T11, 11),
(T12, 12),
(T13, 13),
(T14, 14),
(T15, 15)
);
#[cfg(test)]
mod tests {
use crate::{Optionable, OptionableConvert};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec;
#[cfg(feature = "std")]
use std::vec;
import_std_or_alloc!(vec::{Vec});
import_std_or_alloc!(string::{String});
import_std_or_alloc!(borrow::{Cow,ToOwned});
use crate::Error;
#[cfg(feature = "std")]
use std::collections::{BTreeMap, HashMap};
#[test]
fn primitive_types_optioned_self() {
let a: i32 = 10;
let _: <i32 as Optionable>::Optioned = a;
}
#[test]
fn str() {
let a = "hello";
let _: <&str as Optionable>::Optioned = a;
}
#[test]
fn ptr() {
let a = 2;
let _: <&i32 as Optionable>::Optioned = &a;
}
#[test]
fn array() {
let mut a = [1, 2, 3];
let _: <[i32; 3] as Optionable>::Optioned = a;
let b = [4, 5, 6];
a.merge(b).unwrap();
assert_eq!(a, [4, 5, 6]);
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn container() {
let a = vec![1, 2, 3];
let _: <Vec<i64> as Optionable>::Optioned = a;
}
#[test]
#[cfg(any(feature = "alloc", feature = "std"))]
fn cow() {
use crate::OptionableConvert;
let a = Cow::Owned("hello".to_owned());
let _: <Cow<String> as Optionable>::Optioned = a;
let b: Cow<String> = Cow::Borrowed(&a);
let _: <Cow<String> as Optionable>::Optioned = b;
let c: Cow<str> = Cow::Borrowed("hello");
let _: <Cow<str> as Optionable>::Optioned = c;
let mut a: Cow<String> = Cow::Owned("hello2".to_owned());
a.merge(b.clone()).unwrap();
assert_eq!(a.as_str(), b.as_str());
assert_eq!(a.as_str(), "hello");
}
#[test]
fn result() {
let a = Ok::<_, Error>(42);
let _: <Result<i32, _> as Optionable>::Optioned = a;
}
#[test]
#[cfg(feature = "std")]
fn map() {
let a = HashMap::from([(1, "a".to_owned())]);
let _: <HashMap<i32, String> as Optionable>::Optioned = a;
let a = BTreeMap::from([(1, "a".to_owned())]);
let _: <BTreeMap<i32, String> as Optionable>::Optioned = a;
}
}