summaryrefslogtreecommitdiff
path: root/vendor/enum-ordinalize-derive/src/lib.rs
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
committermo khan <mo@mokhan.ca>2025-07-02 18:36:06 -0600
commit8cdfa445d6629ffef4cb84967ff7017654045bc2 (patch)
tree22f0b0907c024c78d26a731e2e1f5219407d8102 /vendor/enum-ordinalize-derive/src/lib.rs
parent4351c74c7c5f97156bc94d3a8549b9940ac80e3f (diff)
chore: add vendor directory
Diffstat (limited to 'vendor/enum-ordinalize-derive/src/lib.rs')
-rw-r--r--vendor/enum-ordinalize-derive/src/lib.rs808
1 files changed, 808 insertions, 0 deletions
diff --git a/vendor/enum-ordinalize-derive/src/lib.rs b/vendor/enum-ordinalize-derive/src/lib.rs
new file mode 100644
index 00000000..5af4b2ca
--- /dev/null
+++ b/vendor/enum-ordinalize-derive/src/lib.rs
@@ -0,0 +1,808 @@
+/*!
+# Enum Ordinalize Derive
+
+This library enables enums to not only obtain the ordinal values of their variants but also allows for the construction of enums from an ordinal value. See the [`enum-ordinalize`](https://crates.io/crates/enum-ordinalize) crate.
+*/
+
+#![no_std]
+
+#[macro_use]
+extern crate alloc;
+
+mod int128;
+mod int_wrapper;
+mod panic;
+mod variant_type;
+
+use alloc::{string::ToString, vec::Vec};
+
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{
+ parse::{Parse, ParseStream},
+ parse_macro_input,
+ punctuated::Punctuated,
+ spanned::Spanned,
+ Data, DeriveInput, Expr, Fields, Ident, Lit, Meta, Token, UnOp, Visibility,
+};
+use variant_type::VariantType;
+
+use crate::{int128::Int128, int_wrapper::IntWrapper};
+
+#[proc_macro_derive(Ordinalize, attributes(ordinalize))]
+pub fn ordinalize_derive(input: TokenStream) -> TokenStream {
+ struct ConstMember {
+ vis: Option<Visibility>,
+ ident: Ident,
+ meta: Vec<Meta>,
+ function: bool,
+ }
+
+ impl Parse for ConstMember {
+ #[inline]
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let vis = input.parse::<Visibility>().ok();
+
+ let _ = input.parse::<Token![const]>();
+
+ let function = input.parse::<Token![fn]>().is_ok();
+
+ let ident = input.parse::<Ident>()?;
+
+ let mut meta = Vec::new();
+
+ if !input.is_empty() {
+ input.parse::<Token![,]>()?;
+
+ if !input.is_empty() {
+ let result = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
+
+ let mut has_inline = false;
+
+ for m in result {
+ if m.path().is_ident("inline") {
+ has_inline = true;
+ }
+
+ meta.push(m);
+ }
+
+ if !has_inline {
+ meta.push(syn::parse_str("inline")?);
+ }
+ }
+ }
+
+ Ok(Self {
+ vis,
+ ident,
+ meta,
+ function,
+ })
+ }
+ }
+
+ struct ConstFunctionMember {
+ vis: Option<Visibility>,
+ ident: Ident,
+ meta: Vec<Meta>,
+ }
+
+ impl Parse for ConstFunctionMember {
+ #[inline]
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let vis = input.parse::<Visibility>().ok();
+
+ let _ = input.parse::<Token![const]>();
+
+ input.parse::<Token![fn]>()?;
+
+ let ident = input.parse::<Ident>()?;
+
+ let mut meta = Vec::new();
+
+ if !input.is_empty() {
+ input.parse::<Token![,]>()?;
+
+ if !input.is_empty() {
+ let result = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
+
+ let mut has_inline = false;
+
+ for m in result {
+ if m.path().is_ident("inline") {
+ has_inline = true;
+ }
+
+ meta.push(m);
+ }
+
+ if !has_inline {
+ meta.push(syn::parse_str("inline")?);
+ }
+ }
+ }
+
+ Ok(Self {
+ vis,
+ ident,
+ meta,
+ })
+ }
+ }
+
+ struct MyDeriveInput {
+ ast: DeriveInput,
+ variant_type: VariantType,
+ values: Vec<IntWrapper>,
+ variant_idents: Vec<Ident>,
+ use_constant_counter: bool,
+ enable_trait: bool,
+ enable_variant_count: Option<ConstMember>,
+ enable_variants: Option<ConstMember>,
+ enable_values: Option<ConstMember>,
+ enable_from_ordinal_unsafe: Option<ConstFunctionMember>,
+ enable_from_ordinal: Option<ConstFunctionMember>,
+ enable_ordinal: Option<ConstFunctionMember>,
+ }
+
+ impl Parse for MyDeriveInput {
+ fn parse(input: ParseStream) -> syn::Result<Self> {
+ let ast = input.parse::<DeriveInput>()?;
+
+ let mut variant_type = VariantType::default();
+ let mut enable_trait = cfg!(feature = "traits");
+ let mut enable_variant_count = None;
+ let mut enable_variants = None;
+ let mut enable_values = None;
+ let mut enable_from_ordinal_unsafe = None;
+ let mut enable_from_ordinal = None;
+ let mut enable_ordinal = None;
+
+ for attr in ast.attrs.iter() {
+ let path = attr.path();
+
+ if let Some(ident) = path.get_ident() {
+ match ident.to_string().as_str() {
+ "repr" => {
+ // #[repr(u8)], #[repr(u16)], ..., etc.
+ if let Meta::List(list) = &attr.meta {
+ let result = list.parse_args_with(
+ Punctuated::<Ident, Token![,]>::parse_terminated,
+ )?;
+
+ if let Some(value) = result.into_iter().next() {
+ variant_type = VariantType::from_str(value.to_string());
+ }
+ }
+
+ break;
+ },
+ "ordinalize" => {
+ if let Meta::List(list) = &attr.meta {
+ let result = list.parse_args_with(
+ Punctuated::<Meta, Token![,]>::parse_terminated,
+ )?;
+
+ for meta in result {
+ let path = meta.path();
+
+ if let Some(ident) = path.get_ident() {
+ match ident.to_string().as_str() {
+ "impl_trait" => {
+ if let Meta::NameValue(meta) = &meta {
+ if let Expr::Lit(lit) = &meta.value {
+ if let Lit::Bool(value) = &lit.lit {
+ if cfg!(feature = "traits") {
+ enable_trait = value.value;
+ }
+ } else {
+ return Err(
+ panic::bool_attribute_usage(
+ ident,
+ ident.span(),
+ ),
+ );
+ }
+ } else {
+ return Err(panic::bool_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ } else {
+ return Err(panic::bool_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "variant_count" => {
+ if let Meta::List(list) = &meta {
+ enable_variant_count = Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "variants" => {
+ if let Meta::List(list) = &meta {
+ enable_variants = Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "values" => {
+ if let Meta::List(list) = &meta {
+ enable_values = Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "from_ordinal_unsafe" => {
+ if let Meta::List(list) = &meta {
+ enable_from_ordinal_unsafe =
+ Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "from_ordinal" => {
+ if let Meta::List(list) = &meta {
+ enable_from_ordinal = Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ "ordinal" => {
+ if let Meta::List(list) = &meta {
+ enable_ordinal = Some(list.parse_args()?);
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ },
+ _ => {
+ return Err(panic::sub_attributes_for_ordinalize(
+ ident.span(),
+ ));
+ },
+ }
+ } else {
+ return Err(panic::list_attribute_usage(
+ ident,
+ ident.span(),
+ ));
+ }
+ }
+ } else {
+ return Err(panic::list_attribute_usage(ident, ident.span()));
+ }
+ },
+ _ => (),
+ }
+ }
+ }
+
+ let name = &ast.ident;
+
+ if let Data::Enum(data) = &ast.data {
+ let variant_count = data.variants.len();
+
+ if variant_count == 0 {
+ return Err(panic::no_variant(name.span()));
+ }
+
+ let mut values: Vec<IntWrapper> = Vec::with_capacity(variant_count);
+ let mut variant_idents: Vec<Ident> = Vec::with_capacity(variant_count);
+
+ let mut use_constant_counter = false;
+
+ if let VariantType::NonDetermined = variant_type {
+ let mut min = i128::MAX;
+ let mut max = i128::MIN;
+ let mut counter = 0;
+
+ for variant in data.variants.iter() {
+ if let Fields::Unit = variant.fields {
+ if let Some((_, exp)) = variant.discriminant.as_ref() {
+ match exp {
+ Expr::Lit(lit) => {
+ if let Lit::Int(lit) = &lit.lit {
+ counter = lit.base10_parse().map_err(|error| {
+ syn::Error::new(lit.span(), error)
+ })?;
+ } else {
+ return Err(panic::unsupported_discriminant(
+ lit.span(),
+ ));
+ }
+ },
+ Expr::Unary(unary) => {
+ if let UnOp::Neg(_) = unary.op {
+ match unary.expr.as_ref() {
+ Expr::Lit(lit) => {
+ if let Lit::Int(lit) = &lit.lit {
+ match lit.base10_parse::<i128>() {
+ Ok(i) => {
+ counter = -i;
+ },
+ Err(error) => {
+ // overflow
+ if lit.base10_digits() == "170141183460469231731687303715884105728" {
+ counter = i128::MIN;
+ } else {
+ return Err(syn::Error::new(lit.span(), error));
+ }
+ },
+ }
+ } else {
+ return Err(panic::unsupported_discriminant(lit.span()));
+ }
+ },
+ Expr::Path(_)
+ | Expr::Cast(_)
+ | Expr::Binary(_)
+ | Expr::Call(_) => {
+ return Err(panic::constant_variable_on_non_determined_size_enum(unary.expr.span()))
+ },
+ _ => return Err(panic::unsupported_discriminant(unary.expr.span())),
+ }
+ } else {
+ return Err(panic::unsupported_discriminant(
+ unary.op.span(),
+ ));
+ }
+ },
+ Expr::Path(_)
+ | Expr::Cast(_)
+ | Expr::Binary(_)
+ | Expr::Call(_) => {
+ return Err(
+ panic::constant_variable_on_non_determined_size_enum(
+ exp.span(),
+ ),
+ )
+ },
+ _ => return Err(panic::unsupported_discriminant(exp.span())),
+ }
+ };
+
+ if min > counter {
+ min = counter;
+ }
+
+ if max < counter {
+ max = counter;
+ }
+
+ variant_idents.push(variant.ident.clone());
+
+ values.push(IntWrapper::from(counter));
+
+ counter = counter.saturating_add(1);
+ } else {
+ return Err(panic::not_unit_variant(variant.span()));
+ }
+ }
+
+ if min >= i8::MIN as i128 && max <= i8::MAX as i128 {
+ variant_type = VariantType::I8;
+ } else if min >= i16::MIN as i128 && max <= i16::MAX as i128 {
+ variant_type = VariantType::I16;
+ } else if min >= i32::MIN as i128 && max <= i32::MAX as i128 {
+ variant_type = VariantType::I32;
+ } else if min >= i64::MIN as i128 && max <= i64::MAX as i128 {
+ variant_type = VariantType::I64;
+ } else {
+ variant_type = VariantType::I128;
+ }
+ } else {
+ let mut counter = Int128::ZERO;
+ let mut constant_counter = 0;
+ let mut last_exp: Option<&Expr> = None;
+
+ for variant in data.variants.iter() {
+ if let Fields::Unit = variant.fields {
+ if let Some((_, exp)) = variant.discriminant.as_ref() {
+ match exp {
+ Expr::Lit(lit) => {
+ if let Lit::Int(lit) = &lit.lit {
+ counter = lit.base10_parse().map_err(|error| {
+ syn::Error::new(lit.span(), error)
+ })?;
+
+ values.push(IntWrapper::from(counter));
+
+ counter.inc();
+
+ last_exp = None;
+ } else {
+ return Err(panic::unsupported_discriminant(
+ lit.span(),
+ ));
+ }
+ },
+ Expr::Unary(unary) => {
+ if let UnOp::Neg(_) = unary.op {
+ match unary.expr.as_ref() {
+ Expr::Lit(lit) => {
+ if let Lit::Int(lit) = &lit.lit {
+ counter = -lit.base10_parse().map_err(
+ |error| {
+ syn::Error::new(lit.span(), error)
+ },
+ )?;
+
+ values.push(IntWrapper::from(counter));
+
+ counter.inc();
+
+ last_exp = None;
+ } else {
+ return Err(
+ panic::unsupported_discriminant(
+ lit.span(),
+ ),
+ );
+ }
+ },
+ Expr::Path(_) => {
+ values.push(IntWrapper::from((exp, 0)));
+
+ last_exp = Some(exp);
+ constant_counter = 1;
+ },
+ Expr::Cast(_) | Expr::Binary(_) | Expr::Call(_) => {
+ values.push(IntWrapper::from((exp, 0)));
+
+ last_exp = Some(exp);
+ constant_counter = 1;
+
+ use_constant_counter = true;
+ },
+ _ => {
+ return Err(panic::unsupported_discriminant(
+ exp.span(),
+ ));
+ },
+ }
+ } else {
+ return Err(panic::unsupported_discriminant(
+ unary.op.span(),
+ ));
+ }
+ },
+ Expr::Path(_) => {
+ values.push(IntWrapper::from((exp, 0)));
+
+ last_exp = Some(exp);
+ constant_counter = 1;
+ },
+ Expr::Cast(_) | Expr::Binary(_) | Expr::Call(_) => {
+ values.push(IntWrapper::from((exp, 0)));
+
+ last_exp = Some(exp);
+ constant_counter = 1;
+
+ use_constant_counter = true;
+ },
+ _ => return Err(panic::unsupported_discriminant(exp.span())),
+ }
+ } else if let Some(exp) = last_exp {
+ values.push(IntWrapper::from((exp, constant_counter)));
+
+ constant_counter += 1;
+
+ use_constant_counter = true;
+ } else {
+ values.push(IntWrapper::from(counter));
+
+ counter.inc();
+ }
+
+ variant_idents.push(variant.ident.clone());
+ } else {
+ return Err(panic::not_unit_variant(variant.span()));
+ }
+ }
+ }
+
+ Ok(MyDeriveInput {
+ ast,
+ variant_type,
+ values,
+ variant_idents,
+ use_constant_counter,
+ enable_trait,
+ enable_variant_count,
+ enable_variants,
+ enable_values,
+ enable_from_ordinal_unsafe,
+ enable_from_ordinal,
+ enable_ordinal,
+ })
+ } else {
+ Err(panic::not_enum(ast.ident.span()))
+ }
+ }
+ }
+
+ // Parse the token stream
+ let derive_input = parse_macro_input!(input as MyDeriveInput);
+
+ let MyDeriveInput {
+ ast,
+ variant_type,
+ values,
+ variant_idents,
+ use_constant_counter,
+ enable_trait,
+ enable_variant_count,
+ enable_variants,
+ enable_values,
+ enable_ordinal,
+ enable_from_ordinal_unsafe,
+ enable_from_ordinal,
+ } = derive_input;
+
+ // Get the identifier of the type.
+ let name = &ast.ident;
+
+ let variant_count = values.len();
+
+ let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
+
+ // Build the code
+ let mut expanded = proc_macro2::TokenStream::new();
+
+ if enable_trait {
+ #[cfg(feature = "traits")]
+ {
+ let from_ordinal_unsafe = if variant_count == 1 {
+ let variant_ident = &variant_idents[0];
+
+ quote! {
+ #[inline]
+ unsafe fn from_ordinal_unsafe(_number: #variant_type) -> Self {
+ Self::#variant_ident
+ }
+ }
+ } else {
+ quote! {
+ #[inline]
+ unsafe fn from_ordinal_unsafe(number: #variant_type) -> Self {
+ ::core::mem::transmute(number)
+ }
+ }
+ };
+
+ let from_ordinal = if use_constant_counter {
+ quote! {
+ #[inline]
+ fn from_ordinal(number: #variant_type) -> Option<Self> {
+ if false {
+ unreachable!()
+ } #( else if number == #values {
+ Some(Self::#variant_idents)
+ } )* else {
+ None
+ }
+ }
+ }
+ } else {
+ quote! {
+ #[inline]
+ fn from_ordinal(number: #variant_type) -> Option<Self> {
+ match number{
+ #(
+ #values => Some(Self::#variant_idents),
+ )*
+ _ => None
+ }
+ }
+ }
+ };
+
+ expanded.extend(quote! {
+ impl #impl_generics Ordinalize for #name #ty_generics #where_clause {
+ type VariantType = #variant_type;
+
+ const VARIANT_COUNT: usize = #variant_count;
+
+ const VARIANTS: &'static [Self] = &[#( Self::#variant_idents, )*];
+
+ const VALUES: &'static [#variant_type] = &[#( #values, )*];
+
+ #[inline]
+ fn ordinal(&self) -> #variant_type {
+ match self {
+ #(
+ Self::#variant_idents => #values,
+ )*
+ }
+ }
+
+ #from_ordinal_unsafe
+
+ #from_ordinal
+ }
+ });
+ }
+ }
+
+ let mut expanded_2 = proc_macro2::TokenStream::new();
+
+ if let Some(ConstMember {
+ vis,
+ ident,
+ meta,
+ function,
+ }) = enable_variant_count
+ {
+ expanded_2.extend(if function {
+ quote! {
+ #(#[#meta])*
+ #vis const fn #ident () -> usize {
+ #variant_count
+ }
+ }
+ } else {
+ quote! {
+ #(#[#meta])*
+ #vis const #ident: usize = #variant_count;
+ }
+ });
+ }
+
+ if let Some(ConstMember {
+ vis,
+ ident,
+ meta,
+ function,
+ }) = enable_variants
+ {
+ expanded_2.extend(if function {
+ quote! {
+ #(#[#meta])*
+ #vis const fn #ident () -> [Self; #variant_count] {
+ [#( Self::#variant_idents, )*]
+ }
+ }
+ } else {
+ quote! {
+ #(#[#meta])*
+ #vis const #ident: [Self; #variant_count] = [#( Self::#variant_idents, )*];
+ }
+ });
+ }
+
+ if let Some(ConstMember {
+ vis,
+ ident,
+ meta,
+ function,
+ }) = enable_values
+ {
+ expanded_2.extend(if function {
+ quote! {
+ #(#[#meta])*
+ #vis const fn #ident () -> [#variant_type; #variant_count] {
+ [#( #values, )*]
+ }
+ }
+ } else {
+ quote! {
+ #(#[#meta])*
+ #vis const #ident: [#variant_type; #variant_count] = [#( #values, )*];
+ }
+ });
+ }
+
+ if let Some(ConstFunctionMember {
+ vis,
+ ident,
+ meta,
+ }) = enable_from_ordinal_unsafe
+ {
+ let from_ordinal_unsafe = if variant_count == 1 {
+ let variant_ident = &variant_idents[0];
+
+ quote! {
+ #(#[#meta])*
+ #vis const unsafe fn #ident (_number: #variant_type) -> Self {
+ Self::#variant_ident
+ }
+ }
+ } else {
+ quote! {
+ #(#[#meta])*
+ #vis const unsafe fn #ident (number: #variant_type) -> Self {
+ ::core::mem::transmute(number)
+ }
+ }
+ };
+
+ expanded_2.extend(from_ordinal_unsafe);
+ }
+
+ if let Some(ConstFunctionMember {
+ vis,
+ ident,
+ meta,
+ }) = enable_from_ordinal
+ {
+ let from_ordinal = if use_constant_counter {
+ quote! {
+ #(#[#meta])*
+ #vis const fn #ident (number: #variant_type) -> Option<Self> {
+ if false {
+ unreachable!()
+ } #( else if number == #values {
+ Some(Self::#variant_idents)
+ } )* else {
+ None
+ }
+ }
+ }
+ } else {
+ quote! {
+ #(#[#meta])*
+ #vis const fn #ident (number: #variant_type) -> Option<Self> {
+ match number{
+ #(
+ #values => Some(Self::#variant_idents),
+ )*
+ _ => None
+ }
+ }
+ }
+ };
+
+ expanded_2.extend(from_ordinal);
+ }
+
+ if let Some(ConstFunctionMember {
+ vis,
+ ident,
+ meta,
+ }) = enable_ordinal
+ {
+ expanded_2.extend(quote! {
+ #(#[#meta])*
+ #vis const fn #ident (&self) -> #variant_type {
+ match self {
+ #(
+ Self::#variant_idents => #values,
+ )*
+ }
+ }
+ });
+ }
+
+ if !expanded_2.is_empty() {
+ expanded.extend(quote! {
+ impl #impl_generics #name #ty_generics #where_clause {
+ #expanded_2
+ }
+ });
+ }
+
+ expanded.into()
+}