Skip to content

Instantly share code, notes, and snippets.

@novafacing
Last active September 15, 2024 22:29
Show Gist options
  • Save novafacing/aad21dcad051c6f2f616895844eceef7 to your computer and use it in GitHub Desktop.
Save novafacing/aad21dcad051c6f2f616895844eceef7 to your computer and use it in GitHub Desktop.
#[macro_export]
// https://stackoverflow.com/a/70222282
macro_rules! field_size {
($t:ident :: $field:ident) => {{
let m = core::mem::MaybeUninit::<$t>::uninit();
// According to https://doc.rust-lang.org/stable/std/ptr/macro.addr_of_mut.html#examples,
// you can dereference an uninitialized MaybeUninit pointer in addr_of!
// Raw pointer deref in const contexts is stabilized in 1.58:
// https://github.com/rust-lang/rust/pull/89551
let p = unsafe { core::ptr::addr_of!((*(&m as *const _ as *const $t)).$field) };
const fn size_of_raw<T>(_: *const T) -> usize {
core::mem::size_of::<T>()
}
size_of_raw(p)
}};
}
#[macro_export]
macro_rules! from_primitive_patterns {
{
$match_ty:ty,
$match_val:expr,
@parse {$($eout:tt)*},
#[$variant_meta:meta] $($rest:tt)*
} => {
$crate::from_primitive_patterns! {
$match_ty,
$match_val,
@parse {
$($eout)*
},
$($rest)*
}
};
{
$match_ty:ty,
$match_val:expr,
@parse {$($eout:tt)*},
$variant:ident = $value:expr,
$($rest:tt)*
} => {
$crate::from_primitive_patterns! {
$match_ty,
$match_val,
@parse {
$($eout)*
if ($value as $match_ty) == $match_val {
return Some(Self::$variant);
}
},
$($rest)*
}
};
{
$match_ty:ty,
$match_val:expr,
@parse {$($eout:tt)*},
$variant:ident($param:ty),
$($rest:tt)*
} => {
$crate::from_primitive_patterns! {
$match_ty,
$match_val,
@parse {
$($eout)*
Some(Self::$variant($match_val as $param))
},
$($rest)*
}
};
{
$match_ty:ty,
$match_val:expr,
@parse {$($eout:tt)*},
} => {
$($eout)*
};
}
#[macro_export]
macro_rules! to_primitive_patterns {
{
$self:ident,
$match_ty:ty,
@parse {$($eout:tt)*},
#[$variant_meta:meta] $($rest:tt)*
} => {
$crate::to_primitive_patterns! {
$self,
$match_ty,
@parse {
$($eout)*
},
$($rest)*
}
};
{
$self:ident,
$match_ty:ty,
@parse {$($eout:tt)*},
$variant:ident = $value:expr,
$($rest:tt)*
} => {
$crate::to_primitive_patterns! {
$self,
$match_ty,
@parse {
$($eout)*
Self::$variant => Some($value as $match_ty),
},
$($rest)*
}
};
{
$self:ident,
$match_ty:ty,
@parse {$($eout:tt)*},
$variant:ident($param:ty),
$($rest:tt)*
} => {
$crate::to_primitive_patterns! {
$self,
$match_ty,
@parse {
$($eout)*
Self::$variant(value) => Some(*value as $match_ty),
},
$($rest)*
}
};
{
$self:ident,
$match_ty:ty,
@parse {$($eout:tt)*},
} => {
match $self {
$($eout)*
}
};
}
#[macro_export]
macro_rules! parse_enum_variant {
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*},
#[$variant_meta:meta] $($rest:tt)*
} => {
$crate::parse_enum_variant! {
$(#[$enum_meta])*,
$vis,
$name,
$(<$($generic_param),*>)?,
@where_clauses {$($where)*},
@parse {
$($eout)*
#[$variant_meta]
},
$($rest)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*},
$variant:ident,
$($rest:tt)*
} => {
$crate::parse_enum_variant! {
$(#[$enum_meta])*,
$vis,
$name,
$(<$($generic_param),*>)?,
@where_clauses {$($where)*},
@parse {
$($eout)*
$variant,
},
$($rest)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*},
$variant:ident = $value:expr,
$($rest:tt)*
} => {
$crate::parse_enum_variant!{
$(#[$enum_meta])*,
$vis,
$name,
$(<$($generic_param),*>)?,
@where_clauses {$($where)*},
@parse {
$($eout)*
$variant = $value,
},
$($rest)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*},
$variant:ident($param:ty),
$($rest:tt)*
} => {
$crate::parse_enum_variant!{
$(#[$enum_meta])*,
$vis,
$name,
$(<$($generic_param),*>)?,
@where_clauses {$($where)*},
@parse {
$($eout)*
$variant($param),
},
$($rest)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*},
$variant:ident { $($fields:tt)* },
$($rest:tt)*
} => {
$crate::parse_enum_variant!{
$(#[$enum_meta])*,
$vis,
$name,
$(<$($generic_param),*>)?,
@where_clauses {$($where)*},
@parse {
$($eout)*
$variant { $($fields)* },
},
$($rest)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*} $(,)*
} => {
$(#[$enum_meta])*
$vis enum $name
$(<$($generic_param),*>)?
$($where)*
{
$($eout)*
}
impl $(<$($generic_param),*>)? num_traits::FromPrimitive for $name $(<$($generic_param),*>)? $($where:tt)* {
fn from_i64(n: i64) -> Option<Self> {
$crate::from_primitive_patterns! {
i64,
n,
@parse {},
$($eout)*
}
}
fn from_u64(n: u64) -> Option<Self> {
$crate::from_primitive_patterns! {
u64,
n,
@parse {},
$($eout)*
}
}
}
impl $(<$($generic_param),*>)? num_traits::ToPrimitive for $name $(<$($generic_param),*>)? $($where:tt)* {
fn to_i64(&self) -> Option<i64> {
$crate::to_primitive_patterns! {
self,
i64,
@parse {},
$($eout)*
}
}
fn to_u64(&self) -> Option<u64> {
$crate::to_primitive_patterns! {
self,
u64,
@parse {},
$($eout)*
}
}
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*} $(,)*
} => {
$(#[$enum_meta])*
$vis enum $name
$(<$($generic_param),*>)?
$($where)*
{
$($eout)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*} $(,)*
} => {
$(#[$enum_meta])*
$vis enum $name
$(<$($generic_param),*>)?
$($where)*
{
$($eout)*
}
};
{
$(#[$enum_meta:meta])*,
$vis:vis,
$name:ident,
$(<$($generic_param:tt),*>)?,
@where_clauses {$($where:tt)*},
@parse {$($eout:tt)*} $(,)*
} => {
$(#[$enum_meta])*
$vis enum $name
$(<$($generic_param),*>)?
$($where)*
{
$($eout)*
}
};
}
#[macro_export]
/// Add the ability to convert a primitive to an enum with ToPrimitive/FromPrimitive.
/// A trailing 1-element tuple variant is used as a fallback if one is found.
macro_rules! convert_primitive {
(
$(#[$enum_meta:meta])*
$vis:vis enum $enum_name:ident
$(<$($generic_param:tt),*>)?
{
$($variants:tt)*
}
) => {
$crate::parse_enum_variant! {
$(#[$enum_meta])*,
$vis,
$enum_name,
$(<$($generic_param),*>)?,
@where_clauses {},
@parse {}, $($variants)*
}
};
(
$(#[$enum_meta:meta])*
$vis:vis enum $enum_name:ident
$(<$($generic_param:tt),*>)?
where
$where_clause:tt $(<$($where_generic_param:tt),*>)?:
$where_clause_bound:tt $(<$($where_clause_bound_generic_param:tt),*>)?
$(+ $where_clause_bound_plus:tt $(<$($where_clause_bound_generic_param_plus:tt),*>)?)*
$(, $more_where_clause:tt $(<$($more_where_generic_param:tt),*>)?:
$more_where_clause_bound:tt $(<$($more_where_clause_bound_generic_param:tt),*>)?
$(+ $more_where_clause_bound_plus:tt $(<$($more_where_clause_bounds_generic_param_plus:tt),*>)?)* )*
{
$($variants:tt)*
}
) => {
$crate::parse_enum_variant! {
$(#[$enum_meta])*,
$vis,
$enum_name,
$(<$($generic_param),*>)?,
@where_clauses {
where
$where_clause $(<$($where_generic_param),*>)?:
$where_clause_bound $(<$($where_clause_bound_generic_param),*>)?
$(+ $where_clause_bound_plus $(<$($where_clause_bound_generic_param_plus),*>)?)*
$(, $more_where_clause $(<$($more_where_generic_param),*>)?:
$more_where_clause_bound $(<$($more_where_clause_bound_generic_param),*>)?
$(+ $more_where_clause_bound_plus $(<$($more_where_clause_bounds_generic_param_plus),*>)?)* )*
},
@parse {}, $($variants)*
}
};
}
@novafacing
Copy link
Author

Updated to not use match() in from_primitive_patterns because it wasn't working with externally-defined constants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment