impl_oneof!();
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
#[allow(clippy::crate_in_macro_def, reason = "impl_const_init arm")]
macro_rules! impl_oneof {
() => { $crate::impl_oneof!(%canonical); };
($($T:ident : $idx:literal + $nth:literal : $suf:literal),* $(,)?) => {
$crate::impl_oneof!(define_enum: $($T:$nth:$suf),+);
$crate::impl_oneof!(impl_default: $($T),+);
$crate::impl_oneof!(impl_const_init: $($T),+);
$crate::impl_oneof!(methods_general: $($T:$idx+$nth:$suf),+);
$crate::impl_oneof!(methods_individ: $($T:$idx+$nth:$suf),+);
};
(impl_const_init) => {
$crate::impl_oneof!(%canonical %map_ident impl_const_init:);
};
(impl_const_init: $_0:ident $(, $rest:ident)*) => {
impl<const LEN: usize, $_0: crate::ConstInit, $($rest),*> crate::ConstInit
for Oneof<LEN, $_0, $($rest),*> {
const INIT: Self = Oneof::$_0($_0::INIT);
}
};
(%canonical $($prefix:tt)*) => { $crate::impl_oneof! { $($prefix)*
_0:0+1:"st", _1:1+2:"nd", _2:2+3:"rd", _3:3+4:"th", _4:4+5:"th", _5:5+6:"th",
_6:6+7:"th", _7:7+8:"th", _8:8+9:"th", _9:9+10:"th", _10:10+11:"th", _11:11+12:"th",
}};
(%map_ident $prefix:ident:
$($T:ident : $idx:literal + $nth:literal : $suf:literal),* $(,)?) => {
$crate::impl_oneof!($prefix: $($T),*);
};
(%map_ident_nth_suf $prefix:ident:
$($T:ident : $idx:literal + $nth:literal : $suf:literal),* $(,)?) => {
$crate::impl_oneof!($prefix: $($T:$nth:$suf),+);
};
(define_enum: $($variant:ident : $nth:literal : $suf:literal),+) => { $crate::paste! {
#[doc = crate::_tags!(code)]
#[doc = crate::_doc_location!("data/value")]
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Oneof<const LEN: usize, $($variant = ()),+> {
$(
#[doc = "The " $nth $suf " variant."]
$variant($variant)
),+
}
}};
(impl_default: $_0:ident $(, $rest:ident)*) => {
impl<const LEN: usize, $_0: Default, $($rest),*> Default for Oneof<LEN, $_0, $($rest),*> {
fn default() -> Self { Oneof::$_0($_0::default()) }
}
};
(
// Implements:
// - LEN
// - MAX_ARITY
// - variant_index
// - is_variant_index
//
// - validate
//
// - into_tuple_options
// - into_tuple_defaults
// - as_tuple_ref_options
methods_general: $($T:ident : $idx:literal + $nth:literal : $suf:literal),+) => {
impl<const LEN:usize, $($T),+ > Oneof<LEN, $($T),+> {
pub const LEN: usize = {
assert![LEN <= Self::MAX_ARITY, "LEN must be <= MAX_ARITY"];
LEN
};
pub const MAX_ARITY: usize = $crate::ident_total!($($T),+);
pub const fn variant_index(&self) -> usize {
match self { $( Oneof::$T(_) => $idx ),+ }
}
pub const fn is_variant_index(&self, index: usize) -> bool {
self.variant_index() == index
}
}
impl<const LEN: usize, $($T: 'static),+ > Oneof<LEN, $($T),+> {
#[allow(unused_assignments, reason = "wont be read in all cases")]
pub fn validate() -> bool {
let mut non_unit_count = 0;
let mut unit_found = false;
$(
if $crate::TypeId::of::<$T>() == $crate::TypeId::of::<()>() {
unit_found = true;
} else {
if unit_found { return false; }
non_unit_count += 1;
}
)+
LEN == non_unit_count
}
}
impl<const LEN: usize, $($T: Clone),+ > Oneof<LEN, $($T),+> {
pub fn into_tuple_options(self) -> ($(Option<$T>),+) { $crate::paste! {
let index = self.variant_index();
( $(
if $idx == index {
self.clone().[<into $T>]()
} else {
None::<$T>
}
),+ )
}}
pub fn into_tuple_defaults(self) -> ($($T),+) where $($T: Default),+ { $crate::paste! {
let index = self.variant_index();
( $(
if $idx == index {
self.clone().[<into $T>]().unwrap()
} else {
Default::default()
}
),+ )
}}
}
impl<const LEN: usize, $($T),+ > Oneof<LEN, $($T),+> {
pub const fn as_tuple_ref_options(&self) -> ($(Option<&$T>),+) { $crate::paste! {
( $(
if $idx == self.variant_index() {
self.[<as_ref $T>]()
} else {
None::<&$T>
}
),+ )
}}
}
};
(
// Implements methods acting over individual fields.
methods_individ: $($T:ident : $idx:literal + $nth:literal : $suf:literal),+) => {
impl<const LEN: usize, $($T),+> Oneof<LEN, $($T),+> {
$crate::impl_oneof!(methods_field_access: $($T:$idx+$nth:$suf),+);
$crate::impl_oneof!(methods_map: $($T),+);
}
};
(
// implements:
// - is_*
// - into_*
// - as_ref_*
// - as_mut_*
methods_field_access: $($T:ident : $idx:literal + $nth:literal : $suf:literal),+) => {
$( $crate::impl_oneof! { @methods_field_access: $T : $idx + $nth : $suf} )+
};
(@methods_field_access: $T:ident : $idx:literal + $nth:literal : $suf:literal
) => { $crate::paste! {
#[doc = "Returns `true` if there's a value in variant [`"
$T "`](#variant." $T ") (The " $nth $suf ")."]
pub const fn [<is $T>](&self) -> bool { matches!(self, Oneof::$T(_)) }
#[doc = "Returns a shared reference to the inner value in variant `" $T "`, if present."]
pub const fn [<as_ref $T>](&self) -> Option<&$T> {
$crate::is![let Self::$T($T) = self, Some($T), None]
}
#[doc = "Returns an exclusive reference to the inner value in variant`" $T "`, if present."]
pub const fn [<as_mut $T>](&mut self) -> Option<&mut $T> {
$crate::is![let Self::$T($T) = self, Some($T), None]
}
#[doc = "Returns a copy of the value in variant `" $T "`, if present."]
pub const fn [<copy $T >](self) -> Option<$T> where Self: Copy {
$crate::is![let Self::$T($T) = self, Some($T), None]
}
#[doc = "Returns the owned value in variant `" $T "`, if present."]
#[doc = "<hr/>"] pub fn [<into $T>](self) -> Option<$T> {
$crate::is![let Self::$T($T) = self, Some($T), None]
}
}};
(
// implements:
// - map_*
methods_map: $first:ident $(, $rest:ident)*) => {
$crate::impl_oneof!(@methods_map: $first, (), ($($rest),*));
$crate::impl_oneof!(@methods_map_helper: ($first), ($($rest),*));
};
(
@methods_map: $T:ident, ( $($before:ident),* ), ( $($after:ident),* )) => { $crate::paste! {
#[doc = "Transforms the inner value in variant`" $T
"` using `f`, leaving other variants unchanged."]
pub fn [<map $T>]<NEW>(self, f: impl FnOnce($T) -> NEW)
-> Oneof<LEN, $($before,)* NEW, $($after,)* > {
match self {
$( Self::$before(val) => Oneof::$before(val), )*
Self::$T(val) => Oneof::$T(f(val)),
$( Self::$after(val) => Oneof::$after(val), )*
}
}
}};
(@methods_map_helper: ($($before:ident),*), ()) => {};
(@methods_map_helper: ($($before:ident),*), ($first:ident $(, $rest:ident)*)) => {
$crate::impl_oneof!(@methods_map: $first, ($($before),*), ($($rest),*));
$crate::impl_oneof!(@methods_map_helper: ($($before,)* $first), ($($rest),*));
};
}
#[doc(hidden)]
pub use impl_oneof;
#[cfg(test)]
mod tests {
use super::Oneof;
type Bytes = Oneof<2, u8, i8>;
type Unums = Oneof<4, u8, u16, u32, u64>;
#[test]
fn validate() {
assert![Bytes::validate()];
assert![Unums::validate()];
assert![Oneof::<0, (), (), ()>::validate()];
assert![Oneof::<1, i8, (), ()>::validate()];
assert![!Oneof::<0, i8, (), ()>::validate()];
assert![!Oneof::<2, i8, (), ()>::validate()];
assert![!Oneof::<1, (), i8, ()>::validate()];
assert![!Oneof::<2, i32, (), i8>::validate()];
assert![!Oneof::<1, (), (), i8, ()>::validate()];
}
#[test]
fn map() {
let a: Oneof<2, i32, f64> = Oneof::_0(10);
assert_eq![Oneof::_0(20), a.map_0(|v| v * 2)];
assert_eq![Oneof::_0(10), a.map_1(|v| v * 2.0)];
let b: Oneof<2, i32, f64> = Oneof::_1(3.14);
assert_eq![Oneof::_1(3.14), b.map_0(|v| v * 2)];
assert_eq![Oneof::_1(6.28), b.map_1(|v| v * 2.0)];
}
#[test]
fn field_access() {
let mut u = Unums::_2(32);
assert_eq![u.is_2(), true];
assert_eq![u.into_2(), Some(32)];
assert_eq![u.as_ref_2(), Some(&32)];
assert_eq![u.as_mut_2(), Some(&mut 32)];
assert_eq![u.is_0(), false];
assert_eq![u.into_0(), None];
assert_eq![u.as_ref_0(), None];
assert_eq![u.as_mut_0(), None];
}
#[test]
fn positioning() {
let u = Unums::_2(32);
assert_eq![u.variant_index(), 2];
assert_eq![u.is_variant_index(2), true];
assert_eq![u.is_variant_index(3), false];
let u = Unums::_0(32);
assert_eq![u.variant_index(), 0];
assert_eq![u.is_variant_index(0), true];
assert_eq![u.is_variant_index(1), false];
}
#[test]
fn tuple() {
let u = Unums::_2(32);
assert_eq![
u.into_tuple_options(),
(None, None, Some(32), None, None, None, None, None, None, None, None, None)
];
assert_eq![
u.as_tuple_ref_options(),
(None, None, Some(&32), None, None, None, None, None, None, None, None, None)
];
assert_eq![u.into_tuple_defaults(), (0, 0, 32, 0, (), (), (), (), (), (), (), ())];
}
}