├── README.md ├── Cargo.toml ├── .github ├── dependabot.yml └── workflows │ ├── ci-version.yml │ └── ci.yml ├── enum-ordinalize-derive ├── README.md ├── Cargo.toml ├── LICENSE ├── src │ ├── int_wrapper.rs │ ├── variant_type.rs │ ├── panic.rs │ ├── int128.rs │ └── lib.rs ├── rustfmt.toml └── .gitignore ├── enum-ordinalize ├── tests │ ├── impl_ordinal.rs │ ├── impl_from_ordinal.rs │ ├── impl_from_ordinal_unsafe.rs │ ├── derive_nightly.rs │ ├── derive_impl_trait_false.rs │ ├── impl_variant_count.rs │ ├── impl_values.rs │ ├── impl_variants.rs │ ├── derive_advanced.rs │ └── derive.rs ├── Cargo.toml ├── LICENSE ├── rustfmt.toml ├── src │ ├── traits.rs │ └── lib.rs ├── .gitignore └── README.md ├── LICENSE └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | See [enum-ordinalize/README.md](enum-ordinalize/README.md). -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "enum-ordinalize-derive", 5 | "enum-ordinalize" 6 | ] 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /enum-ordinalize-derive/README.md: -------------------------------------------------------------------------------- 1 | Enum Ordinalize Derive 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/enum-ordinalize/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/enum-ordinalize/actions/workflows/ci.yml) 5 | 6 | This crate provides a procedural macro that enables enums to not only obtain the ordinal values of their variants but also allows for the construction of enums from an ordinal value. 7 | 8 | See the [`enum-ordinalize`](https://crates.io/crates/enum-ordinalize) crate. 9 | 10 | ## Crates.io 11 | 12 | https://crates.io/crates/enum-ordinalize 13 | 14 | ## Documentation 15 | 16 | https://docs.rs/enum-ordinalize 17 | 18 | ## License 19 | 20 | [MIT](LICENSE) -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_ordinal.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn ordinal_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(ordinal(pub const fn v, doc = "Retrieve the integer number of this variant."))] 9 | enum MyEnum { 10 | A, 11 | B, 12 | C, 13 | } 14 | 15 | assert_eq!(1, MyEnum::B.v()); 16 | } 17 | 18 | #[test] 19 | fn ordinal_2() { 20 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 21 | #[ordinalize(ordinal(pub fn v, doc = "Retrieve the integer number of this variant."))] 22 | enum MyEnum { 23 | A, 24 | B, 25 | C, 26 | } 27 | 28 | assert_eq!(1, MyEnum::B.v()); 29 | } 30 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_from_ordinal.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn from_ordinal_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(from_ordinal(pub const fn v, doc = "Obtain a variant based on an integer number."))] 9 | enum MyEnum { 10 | A, 11 | B, 12 | C, 13 | } 14 | 15 | assert_eq!(Some(MyEnum::B), MyEnum::v(1)); 16 | } 17 | 18 | #[test] 19 | fn from_ordinal_2() { 20 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 21 | #[ordinalize(from_ordinal(pub fn v, doc = "Obtain a variant based on an integer number."))] 22 | enum MyEnum { 23 | A, 24 | B, 25 | C, 26 | } 27 | 28 | assert_eq!(Some(MyEnum::B), MyEnum::v(1)); 29 | } 30 | -------------------------------------------------------------------------------- /enum-ordinalize-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enum-ordinalize-derive" 3 | version = "4.3.2" 4 | edition = "2021" 5 | rust-version = "1.68" 6 | repository = "https://github.com/magiclen/enum-ordinalize" 7 | homepage = "https://magiclen.org/enum-ordinalize" 8 | keywords = ["enum", "ordinal", "ordinalize", "number"] 9 | categories = ["no-std", "rust-patterns"] 10 | description = "This crate provides a procedural macro that enables enums to not only obtain the ordinal values of their variants but also allows for the construction of enums from an ordinal value." 11 | license = "MIT" 12 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [dependencies] 18 | syn = "2" 19 | quote = "1" 20 | proc-macro2 = "1" 21 | 22 | [features] 23 | default = [] 24 | 25 | traits = [] -------------------------------------------------------------------------------- /enum-ordinalize/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "enum-ordinalize" 3 | version = "4.3.2" 4 | edition = "2021" 5 | rust-version = "1.68" 6 | repository = "https://github.com/magiclen/enum-ordinalize" 7 | homepage = "https://magiclen.org/enum-ordinalize" 8 | keywords = ["enum", "ordinal", "ordinalize", "number"] 9 | categories = ["no-std", "rust-patterns"] 10 | description = "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." 11 | license = "MIT" 12 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 13 | 14 | [dependencies] 15 | enum-ordinalize-derive = { version = "4.3", path = "../enum-ordinalize-derive", default-features = false, optional = true } 16 | 17 | [features] 18 | default = ["derive", "traits"] 19 | 20 | derive = ["dep:enum-ordinalize-derive"] 21 | traits = ["enum-ordinalize-derive?/traits"] 22 | 23 | nightly-test = [] 24 | 25 | [package.metadata.docs.rs] 26 | all-features = true 27 | rustdoc-args = ["--cfg", "docsrs"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /enum-ordinalize/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /enum-ordinalize-derive/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_from_ordinal_unsafe.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn from_ordinal_unsafe_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(from_ordinal_unsafe( 9 | pub const fn v, 10 | doc = "Obtain a variant based on an integer number.", 11 | doc = "# Safety", 12 | doc = "You have to ensure that the input integer number can correspond to a variant on your own.", 13 | ))] 14 | enum MyEnum { 15 | A, 16 | B, 17 | C, 18 | } 19 | 20 | assert_eq!(MyEnum::B, unsafe { MyEnum::v(1) }); 21 | } 22 | 23 | #[test] 24 | fn from_ordinal_unsafe_2() { 25 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 26 | #[ordinalize(from_ordinal_unsafe( 27 | pub fn v, 28 | doc = "Obtain a variant based on an integer number.", 29 | doc = "# Safety", 30 | doc = "You have to ensure that the input integer number can correspond to a variant on your own.", 31 | ))] 32 | enum MyEnum { 33 | A, 34 | B, 35 | C, 36 | } 37 | 38 | assert_eq!(MyEnum::B, unsafe { MyEnum::v(1) }); 39 | } 40 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/derive_nightly.rs: -------------------------------------------------------------------------------- 1 | #![cfg(all(feature = "derive", feature = "traits", feature = "nightly-test"))] 2 | #![allow(incomplete_features)] 3 | #![allow(stable_features)] 4 | 5 | use enum_ordinalize::Ordinalize; 6 | 7 | #[test] 8 | fn create_ordinalized_enum_4_3() { 9 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 10 | #[repr(i128)] 11 | enum MyEnum { 12 | A = 2i128, 13 | B = 4, 14 | C = 75557863725914323419136, 15 | } 16 | 17 | assert_eq!(3, MyEnum::VARIANT_COUNT); 18 | assert_eq!([MyEnum::A, MyEnum::B, MyEnum::C], MyEnum::VARIANTS); 19 | assert_eq!([2i128, 4i128, 75557863725914323419136i128], MyEnum::VALUES); 20 | 21 | assert_eq!(2i128, MyEnum::A.ordinal()); 22 | assert_eq!(4i128, MyEnum::B.ordinal()); 23 | assert_eq!(75557863725914323419136i128, MyEnum::C.ordinal()); 24 | 25 | assert_eq!(Some(MyEnum::A), MyEnum::from_ordinal(2i128)); 26 | assert_eq!(Some(MyEnum::B), MyEnum::from_ordinal(4i128)); 27 | assert_eq!(Some(MyEnum::C), MyEnum::from_ordinal(75557863725914323419136i128)); 28 | 29 | assert_eq!(MyEnum::A, unsafe { MyEnum::from_ordinal_unsafe(2i128) }); 30 | assert_eq!(MyEnum::B, unsafe { MyEnum::from_ordinal_unsafe(4i128) }); 31 | assert_eq!(MyEnum::C, unsafe { MyEnum::from_ordinal_unsafe(75557863725914323419136i128) }); 32 | } 33 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/derive_impl_trait_false.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn create_ordinalized_enum_1_3() { 7 | #[allow(dead_code)] 8 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 9 | #[ordinalize(impl_trait = false)] 10 | #[repr(u8)] 11 | enum MyEnum { 12 | A, 13 | B, 14 | } 15 | 16 | #[cfg(feature = "traits")] 17 | impl Ordinalize for MyEnum { 18 | type VariantType = u8; 19 | 20 | const VALUES: &'static [Self::VariantType] = &[0, 1]; 21 | const VARIANTS: &'static [Self] = &[MyEnum::A, MyEnum::B]; 22 | const VARIANT_COUNT: usize = 2; 23 | 24 | #[inline] 25 | unsafe fn from_ordinal_unsafe(number: Self::VariantType) -> Self { 26 | ::core::mem::transmute(number) 27 | } 28 | 29 | #[inline] 30 | fn from_ordinal(number: Self::VariantType) -> Option { 31 | match number { 32 | 0 => Some(Self::A), 33 | 1 => Some(Self::B), 34 | _ => None, 35 | } 36 | } 37 | 38 | #[inline] 39 | fn ordinal(&self) -> Self::VariantType { 40 | match self { 41 | Self::A => 0, 42 | Self::B => 1, 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_variant_count.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn variant_count_const_fn_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(variant_count(pub const fn v, doc = "Retrieve the count of variants."))] 9 | enum MyEnum { 10 | A, 11 | B, 12 | C, 13 | } 14 | 15 | assert_eq!(3, MyEnum::v()); 16 | } 17 | 18 | #[test] 19 | fn variant_count_const_fn_2() { 20 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 21 | #[ordinalize(variant_count(pub fn v, doc = "Retrieve the count of variants."))] 22 | enum MyEnum { 23 | A, 24 | B, 25 | C, 26 | } 27 | 28 | assert_eq!(3, MyEnum::v()); 29 | } 30 | 31 | #[test] 32 | fn variant_count_const_1() { 33 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 34 | #[ordinalize(variant_count(pub const V, doc = "The count of variants."))] 35 | enum MyEnum { 36 | A, 37 | B, 38 | C, 39 | } 40 | 41 | assert_eq!(3, MyEnum::V); 42 | } 43 | 44 | #[test] 45 | fn variant_count_const_2() { 46 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 47 | #[ordinalize(variant_count(pub V, doc = "The count of variants."))] 48 | enum MyEnum { 49 | A, 50 | B, 51 | C, 52 | } 53 | 54 | assert_eq!(3, MyEnum::V); 55 | } 56 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_values.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn variants_const_fn_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(values(pub const fn v, doc = "Retrieve the array of `MyEnum`'s values."))] 9 | enum MyEnum { 10 | A, 11 | B, 12 | C, 13 | } 14 | 15 | assert_eq!([0i8, 1i8, 2i8], MyEnum::v()); 16 | } 17 | 18 | #[test] 19 | fn variants_const_fn_2() { 20 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 21 | #[ordinalize(values(pub fn v, doc = "Retrieve the array of `MyEnum`'s values."))] 22 | enum MyEnum { 23 | A, 24 | B, 25 | C, 26 | } 27 | 28 | assert_eq!([0i8, 1i8, 2i8], MyEnum::v()); 29 | } 30 | 31 | #[test] 32 | fn variants_const_1() { 33 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 34 | #[ordinalize(values(pub const V, doc = "The array of `MyEnum`'s values."))] 35 | enum MyEnum { 36 | A, 37 | B, 38 | C, 39 | } 40 | 41 | assert_eq!([0i8, 1i8, 2i8], MyEnum::V); 42 | } 43 | 44 | #[test] 45 | fn variants_const_2() { 46 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 47 | #[ordinalize(values(pub V, doc = "The array of `MyEnum`'s values."))] 48 | enum MyEnum { 49 | A, 50 | B, 51 | C, 52 | } 53 | 54 | assert_eq!([0i8, 1i8, 2i8], MyEnum::V); 55 | } 56 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/impl_variants.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "derive")] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn variants_const_fn_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | #[ordinalize(variants(pub const fn v, doc = "Retrieve the array of `MyEnum`'s variants."))] 9 | enum MyEnum { 10 | A, 11 | B, 12 | C, 13 | } 14 | 15 | assert_eq!([MyEnum::A, MyEnum::B, MyEnum::C], MyEnum::v()); 16 | } 17 | 18 | #[test] 19 | fn variants_const_fn_2() { 20 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 21 | #[ordinalize(variants(pub fn v, doc = "Retrieve the array of `MyEnum`'s variants."))] 22 | enum MyEnum { 23 | A, 24 | B, 25 | C, 26 | } 27 | 28 | assert_eq!([MyEnum::A, MyEnum::B, MyEnum::C], MyEnum::v()); 29 | } 30 | 31 | #[test] 32 | fn variants_const_1() { 33 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 34 | #[ordinalize(variants(pub const V, doc = "The array of `MyEnum`'s variants."))] 35 | enum MyEnum { 36 | A, 37 | B, 38 | C, 39 | } 40 | 41 | assert_eq!([MyEnum::A, MyEnum::B, MyEnum::C], MyEnum::V); 42 | } 43 | 44 | #[test] 45 | fn variants_const_2() { 46 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 47 | #[ordinalize(variants(pub V, doc = "The array of `MyEnum`'s variants."))] 48 | enum MyEnum { 49 | A, 50 | B, 51 | C, 52 | } 53 | 54 | assert_eq!([MyEnum::A, MyEnum::B, MyEnum::C], MyEnum::V); 55 | } 56 | -------------------------------------------------------------------------------- /enum-ordinalize-derive/src/int_wrapper.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Literal, TokenStream}; 2 | use quote::{quote, ToTokens, TokenStreamExt}; 3 | use syn::Expr; 4 | 5 | use crate::int128::Int128; 6 | 7 | pub(crate) enum IntWrapper { 8 | Integer(Int128), 9 | Constant(Expr, usize), 10 | } 11 | 12 | impl From for IntWrapper { 13 | #[inline] 14 | fn from(v: Int128) -> IntWrapper { 15 | Self::Integer(v) 16 | } 17 | } 18 | 19 | impl From for IntWrapper { 20 | #[inline] 21 | fn from(v: i128) -> IntWrapper { 22 | Self::Integer(Int128::from(v)) 23 | } 24 | } 25 | 26 | impl From<(&Expr, usize)> for IntWrapper { 27 | #[inline] 28 | fn from((expr, counter): (&Expr, usize)) -> IntWrapper { 29 | Self::Constant(expr.clone(), counter) 30 | } 31 | } 32 | 33 | impl ToTokens for IntWrapper { 34 | #[inline] 35 | fn to_tokens(&self, tokens: &mut TokenStream) { 36 | match self { 37 | Self::Integer(v) => { 38 | let lit = match v { 39 | Int128::Signed(i) => Literal::i128_unsuffixed(*i), 40 | Int128::Unsigned(u) => Literal::u128_unsuffixed(*u), 41 | }; 42 | 43 | tokens.append(lit); 44 | }, 45 | Self::Constant(expr, counter) => { 46 | let counter = *counter; 47 | 48 | if counter > 0 { 49 | tokens.extend(quote!(#expr +)); 50 | tokens.append(Literal::usize_unsuffixed(counter)); 51 | } else { 52 | tokens.extend(quote!(#expr)); 53 | } 54 | }, 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /enum-ordinalize/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /enum-ordinalize-derive/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /enum-ordinalize-derive/src/variant_type.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::{ToTokens, TokenStreamExt}; 3 | 4 | #[derive(Debug)] 5 | pub(crate) enum VariantType { 6 | ISize, 7 | I8, 8 | I16, 9 | I32, 10 | I64, 11 | I128, 12 | USize, 13 | U8, 14 | U16, 15 | U32, 16 | U64, 17 | U128, 18 | NonDetermined, 19 | } 20 | 21 | impl VariantType { 22 | #[inline] 23 | pub(crate) fn from_str>(s: S) -> VariantType { 24 | let s = s.as_ref(); 25 | 26 | match s { 27 | "i8" => VariantType::I8, 28 | "i16" => VariantType::I16, 29 | "i32" => VariantType::I32, 30 | "i64" => VariantType::I64, 31 | "i128" => VariantType::I128, 32 | "isize" => VariantType::ISize, 33 | "u8" => VariantType::U8, 34 | "u16" => VariantType::U16, 35 | "u32" => VariantType::U32, 36 | "u64" => VariantType::U64, 37 | "u128" => VariantType::U128, 38 | "usize" => VariantType::USize, 39 | _ => VariantType::NonDetermined, 40 | } 41 | } 42 | 43 | #[inline] 44 | pub(crate) fn as_str(&self) -> &'static str { 45 | match self { 46 | VariantType::ISize => "isize", 47 | VariantType::I8 => "i8", 48 | VariantType::I16 => "i16", 49 | VariantType::I32 => "i32", 50 | VariantType::I64 => "i64", 51 | VariantType::I128 => "i128", 52 | VariantType::USize => "usize", 53 | VariantType::U8 => "u8", 54 | VariantType::U16 => "u16", 55 | VariantType::U32 => "u32", 56 | VariantType::U64 => "u64", 57 | VariantType::U128 => "u128", 58 | _ => unreachable!(), 59 | } 60 | } 61 | } 62 | 63 | impl Default for VariantType { 64 | #[inline] 65 | fn default() -> Self { 66 | VariantType::NonDetermined 67 | } 68 | } 69 | 70 | impl ToTokens for VariantType { 71 | #[inline] 72 | fn to_tokens(&self, tokens: &mut TokenStream) { 73 | tokens.append(Ident::new(self.as_str(), Span::call_site())); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /enum-ordinalize/src/traits.rs: -------------------------------------------------------------------------------- 1 | /// This trait provides an enum with the ability to not only obtain the ordinal values of its variants but also allows for the construction of enums from an ordinal value. 2 | /// 3 | /// ```rust 4 | /// use enum_ordinalize::Ordinalize; 5 | /// 6 | /// #[repr(u8)] 7 | /// enum E { 8 | /// A, 9 | /// B, 10 | /// } 11 | /// 12 | /// impl Ordinalize for E { 13 | /// type VariantType = u8; 14 | /// 15 | /// const VALUES: &'static [Self::VariantType] = &[0, 1]; 16 | /// const VARIANTS: &'static [Self] = &[E::A, E::B]; 17 | /// const VARIANT_COUNT: usize = 2; 18 | /// 19 | /// #[inline] 20 | /// unsafe fn from_ordinal_unsafe(number: Self::VariantType) -> Self { 21 | /// ::core::mem::transmute(number) 22 | /// } 23 | /// 24 | /// #[inline] 25 | /// fn from_ordinal(number: Self::VariantType) -> Option { 26 | /// match number { 27 | /// 0 => Some(Self::A), 28 | /// 1 => Some(Self::B), 29 | /// _ => None, 30 | /// } 31 | /// } 32 | /// 33 | /// #[inline] 34 | /// fn ordinal(&self) -> Self::VariantType { 35 | /// match self { 36 | /// Self::A => 0, 37 | /// Self::B => 1, 38 | /// } 39 | /// } 40 | /// } 41 | /// ``` 42 | pub trait Ordinalize: Sized + 'static { 43 | /// The type of the values of the variants. 44 | type VariantType; 45 | 46 | /// The count of variants. 47 | const VARIANT_COUNT: usize; 48 | 49 | /// List of this enum's variants. 50 | const VARIANTS: &'static [Self]; 51 | 52 | /// List of values for all variants of this enum. 53 | const VALUES: &'static [Self::VariantType]; 54 | 55 | /// Obtain a variant based on an integer number. 56 | /// 57 | /// # Safety 58 | /// You have to ensure that the input integer number can correspond to a variant on your own. 59 | unsafe fn from_ordinal_unsafe(number: Self::VariantType) -> Self; 60 | 61 | /// Obtain a variant based on an integer number. 62 | fn from_ordinal(number: Self::VariantType) -> Option 63 | where 64 | Self: Sized; 65 | 66 | /// Retrieve the integer number of this variant. 67 | fn ordinal(&self) -> Self::VariantType; 68 | } 69 | -------------------------------------------------------------------------------- /.github/workflows/ci-version.yml: -------------------------------------------------------------------------------- 1 | name: CI-version 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | tests: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - ubuntu-latest 18 | - macos-latest 19 | - windows-latest 20 | toolchain: 21 | - stable 22 | features: 23 | - 24 | - --no-default-features 25 | - --features traits 26 | - --features derive 27 | - --features traits --features derive 28 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 29 | runs-on: ${{ matrix.os }} 30 | steps: 31 | - uses: actions/checkout@v6 32 | - uses: actions-rust-lang/setup-rust-toolchain@v1 33 | with: 34 | toolchain: ${{ matrix.toolchain }} 35 | - run: cargo test --release ${{ matrix.features }} 36 | - run: cargo doc --release ${{ matrix.features }} 37 | 38 | tests-nightly: 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | os: 43 | - ubuntu-latest 44 | - macos-latest 45 | - windows-latest 46 | toolchain: 47 | - nightly 48 | features: 49 | - --features nightly-test 50 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 51 | runs-on: ${{ matrix.os }} 52 | steps: 53 | - uses: actions/checkout@v6 54 | - uses: actions-rust-lang/setup-rust-toolchain@v1 55 | with: 56 | toolchain: ${{ matrix.toolchain }} 57 | - run: cargo test --release ${{ matrix.features }} 58 | - run: cargo doc --release ${{ matrix.features }} 59 | 60 | MSRV: 61 | strategy: 62 | fail-fast: false 63 | matrix: 64 | os: 65 | - ubuntu-latest 66 | - macos-latest 67 | - windows-latest 68 | toolchain: 69 | - "1.68" 70 | features: 71 | - 72 | - --no-default-features 73 | - --features traits 74 | - --features derive 75 | - --features traits --features derive 76 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 77 | runs-on: ${{ matrix.os }} 78 | steps: 79 | - uses: actions/checkout@v6 80 | - uses: actions-rust-lang/setup-rust-toolchain@v1 81 | with: 82 | toolchain: ${{ matrix.toolchain }} 83 | - run: cargo test --release --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /enum-ordinalize-derive/src/panic.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display, Formatter}; 2 | 3 | use proc_macro2::Span; 4 | use syn::Ident; 5 | 6 | struct DisplayStringSlice<'a>(&'a [&'static str]); 7 | 8 | impl<'a> Display for DisplayStringSlice<'a> { 9 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 10 | for &s in self.0 { 11 | f.write_str("\n ")?; 12 | f.write_str(s)?; 13 | } 14 | 15 | Ok(()) 16 | } 17 | } 18 | 19 | #[inline] 20 | pub(crate) fn not_enum(span: Span) -> syn::Error { 21 | syn::Error::new(span, "only enums can be ordinalized") 22 | } 23 | 24 | #[inline] 25 | pub(crate) fn no_variant(span: Span) -> syn::Error { 26 | syn::Error::new(span, "an ordinalized enum needs to have at least one variant") 27 | } 28 | 29 | #[inline] 30 | pub(crate) fn not_unit_variant(span: Span) -> syn::Error { 31 | syn::Error::new(span, "an ordinalized enum can only have unit variants") 32 | } 33 | 34 | #[inline] 35 | pub(crate) fn unsupported_discriminant(span: Span) -> syn::Error { 36 | syn::Error::new( 37 | span, 38 | "the discriminant of a variant of an ordinalized enum needs to be a legal literal \ 39 | integer, a constant variable/function or a constant expression", 40 | ) 41 | } 42 | #[inline] 43 | pub(crate) fn constant_variable_on_non_determined_size_enum(span: Span) -> syn::Error { 44 | syn::Error::new( 45 | span, 46 | "the discriminant of a variant can be assigned not to a literal integer only when the \ 47 | ordinalized enum is using the `repr` attribute to determine it's size before compilation", 48 | ) 49 | } 50 | 51 | #[inline] 52 | pub fn list_attribute_usage(name: &Ident, span: Span) -> syn::Error { 53 | syn::Error::new(span, format!("the `{name}` attribute should be a list")) 54 | } 55 | 56 | #[inline] 57 | pub(crate) fn bool_attribute_usage(name: &Ident, span: Span) -> syn::Error { 58 | syn::Error::new( 59 | span, 60 | format!("the `{name}` attribute should be a name-value pair. The value type is boolean"), 61 | ) 62 | } 63 | 64 | #[inline] 65 | pub(crate) fn sub_attributes_for_ordinalize(span: Span) -> syn::Error { 66 | syn::Error::new( 67 | span, 68 | format!( 69 | "available sub-attributes for the `ordinalize` attribute:{}", 70 | DisplayStringSlice(&[ 71 | "impl_trait", 72 | "variant_count", 73 | "variants", 74 | "values", 75 | "ordinal", 76 | "from_ordinal_unsafe", 77 | "from_ordinal", 78 | ]) 79 | ), 80 | ) 81 | } 82 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | rustfmt: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v6 13 | - uses: actions-rust-lang/setup-rust-toolchain@v1 14 | with: 15 | toolchain: nightly 16 | components: rustfmt 17 | - uses: actions-rust-lang/rustfmt@v1 18 | 19 | clippy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v6 23 | - uses: actions-rust-lang/setup-rust-toolchain@v1 24 | with: 25 | components: clippy 26 | - run: cargo clippy --all-targets -- -D warnings 27 | 28 | tests: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: 33 | - ubuntu-latest 34 | - macos-latest 35 | - windows-latest 36 | toolchain: 37 | - stable 38 | - nightly 39 | features: 40 | - 41 | - --no-default-features 42 | - --features traits 43 | - --features derive 44 | - --features traits --features derive 45 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 46 | runs-on: ${{ matrix.os }} 47 | steps: 48 | - uses: actions/checkout@v6 49 | - uses: actions-rust-lang/setup-rust-toolchain@v1 50 | with: 51 | toolchain: ${{ matrix.toolchain }} 52 | - run: cargo test ${{ matrix.features }} 53 | - run: cargo doc ${{ matrix.features }} 54 | 55 | tests-nightly: 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | os: 60 | - ubuntu-latest 61 | - macos-latest 62 | - windows-latest 63 | toolchain: 64 | - nightly 65 | features: 66 | - --features nightly-test 67 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 68 | runs-on: ${{ matrix.os }} 69 | steps: 70 | - uses: actions/checkout@v6 71 | - uses: actions-rust-lang/setup-rust-toolchain@v1 72 | with: 73 | toolchain: ${{ matrix.toolchain }} 74 | - run: cargo test ${{ matrix.features }} 75 | - run: cargo doc ${{ matrix.features }} 76 | 77 | MSRV: 78 | strategy: 79 | fail-fast: false 80 | matrix: 81 | os: 82 | - ubuntu-latest 83 | - macos-latest 84 | - windows-latest 85 | toolchain: 86 | - "1.68" 87 | features: 88 | - 89 | - --no-default-features 90 | - --features traits 91 | - --features derive 92 | - --features traits --features derive 93 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 94 | runs-on: ${{ matrix.os }} 95 | steps: 96 | - uses: actions/checkout@v6 97 | - uses: actions-rust-lang/setup-rust-toolchain@v1 98 | with: 99 | toolchain: ${{ matrix.toolchain }} 100 | - run: cargo test --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij+all ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignore everything but code style settings and run configurations 82 | # that are supposed to be shared within teams. 83 | 84 | .idea/* 85 | 86 | !.idea/codeStyles 87 | !.idea/runConfigurations 88 | 89 | ### Rust ### 90 | # Generated by Cargo 91 | # will have compiled files and executables 92 | debug/ 93 | target/ 94 | 95 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 96 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 97 | Cargo.lock 98 | 99 | # These are backup files generated by rustfmt 100 | **/*.rs.bk 101 | 102 | # MSVC Windows builds of rustc generate these, which store debugging information 103 | *.pdb 104 | 105 | ### Vim ### 106 | # Swap 107 | [._]*.s[a-v][a-z] 108 | !*.svg # comment out if you don't need vector files 109 | [._]*.sw[a-p] 110 | [._]s[a-rt-v][a-z] 111 | [._]ss[a-gi-z] 112 | [._]sw[a-p] 113 | 114 | # Session 115 | Session.vim 116 | Sessionx.vim 117 | 118 | # Temporary 119 | .netrwhist 120 | *~ 121 | # Auto-generated tag files 122 | tags 123 | # Persistent undo 124 | [._]*.un~ 125 | 126 | ### VisualStudioCode ### 127 | .vscode/* 128 | !.vscode/settings.json 129 | !.vscode/tasks.json 130 | !.vscode/launch.json 131 | !.vscode/extensions.json 132 | !.vscode/*.code-snippets 133 | 134 | # Local History for Visual Studio Code 135 | .history/ 136 | 137 | # Built Visual Studio Code Extensions 138 | *.vsix 139 | 140 | ### VisualStudioCode Patch ### 141 | # Ignore all local history of files 142 | .history 143 | .ionide -------------------------------------------------------------------------------- /enum-ordinalize/.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij+all ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignore everything but code style settings and run configurations 82 | # that are supposed to be shared within teams. 83 | 84 | .idea/* 85 | 86 | !.idea/codeStyles 87 | !.idea/runConfigurations 88 | 89 | ### Rust ### 90 | # Generated by Cargo 91 | # will have compiled files and executables 92 | debug/ 93 | target/ 94 | 95 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 96 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 97 | Cargo.lock 98 | 99 | # These are backup files generated by rustfmt 100 | **/*.rs.bk 101 | 102 | # MSVC Windows builds of rustc generate these, which store debugging information 103 | *.pdb 104 | 105 | ### Vim ### 106 | # Swap 107 | [._]*.s[a-v][a-z] 108 | !*.svg # comment out if you don't need vector files 109 | [._]*.sw[a-p] 110 | [._]s[a-rt-v][a-z] 111 | [._]ss[a-gi-z] 112 | [._]sw[a-p] 113 | 114 | # Session 115 | Session.vim 116 | Sessionx.vim 117 | 118 | # Temporary 119 | .netrwhist 120 | *~ 121 | # Auto-generated tag files 122 | tags 123 | # Persistent undo 124 | [._]*.un~ 125 | 126 | ### VisualStudioCode ### 127 | .vscode/* 128 | !.vscode/settings.json 129 | !.vscode/tasks.json 130 | !.vscode/launch.json 131 | !.vscode/extensions.json 132 | !.vscode/*.code-snippets 133 | 134 | # Local History for Visual Studio Code 135 | .history/ 136 | 137 | # Built Visual Studio Code Extensions 138 | *.vsix 139 | 140 | ### VisualStudioCode Patch ### 141 | # Ignore all local history of files 142 | .history 143 | .ionide -------------------------------------------------------------------------------- /enum-ordinalize-derive/.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij+all ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignore everything but code style settings and run configurations 82 | # that are supposed to be shared within teams. 83 | 84 | .idea/* 85 | 86 | !.idea/codeStyles 87 | !.idea/runConfigurations 88 | 89 | ### Rust ### 90 | # Generated by Cargo 91 | # will have compiled files and executables 92 | debug/ 93 | target/ 94 | 95 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 96 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 97 | Cargo.lock 98 | 99 | # These are backup files generated by rustfmt 100 | **/*.rs.bk 101 | 102 | # MSVC Windows builds of rustc generate these, which store debugging information 103 | *.pdb 104 | 105 | ### Vim ### 106 | # Swap 107 | [._]*.s[a-v][a-z] 108 | !*.svg # comment out if you don't need vector files 109 | [._]*.sw[a-p] 110 | [._]s[a-rt-v][a-z] 111 | [._]ss[a-gi-z] 112 | [._]sw[a-p] 113 | 114 | # Session 115 | Session.vim 116 | Sessionx.vim 117 | 118 | # Temporary 119 | .netrwhist 120 | *~ 121 | # Auto-generated tag files 122 | tags 123 | # Persistent undo 124 | [._]*.un~ 125 | 126 | ### VisualStudioCode ### 127 | .vscode/* 128 | !.vscode/settings.json 129 | !.vscode/tasks.json 130 | !.vscode/launch.json 131 | !.vscode/extensions.json 132 | !.vscode/*.code-snippets 133 | 134 | # Local History for Visual Studio Code 135 | .history/ 136 | 137 | # Built Visual Studio Code Extensions 138 | *.vsix 139 | 140 | ### VisualStudioCode Patch ### 141 | # Ignore all local history of files 142 | .history 143 | .ionide -------------------------------------------------------------------------------- /enum-ordinalize/tests/derive_advanced.rs: -------------------------------------------------------------------------------- 1 | #![cfg(all(feature = "derive", feature = "traits"))] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | const FOUR: i8 = 4; 6 | 7 | const fn eight() -> i8 { 8 | 8 9 | } 10 | 11 | const TEN: i16 = 10; 12 | 13 | #[test] 14 | fn create_ordinalized_enum_5_1() { 15 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 16 | #[repr(i8)] 17 | enum MyEnum { 18 | Two = 1 + 1, 19 | Four = FOUR, 20 | Eight = eight(), 21 | Ten = TEN as i8, 22 | } 23 | 24 | assert_eq!(4, MyEnum::VARIANT_COUNT); 25 | assert_eq!([MyEnum::Two, MyEnum::Four, MyEnum::Eight, MyEnum::Ten], MyEnum::VARIANTS); 26 | assert_eq!([2i8, 4i8, 8i8, 10i8], MyEnum::VALUES); 27 | 28 | assert_eq!(2i8, MyEnum::Two.ordinal()); 29 | assert_eq!(4i8, MyEnum::Four.ordinal()); 30 | assert_eq!(8i8, MyEnum::Eight.ordinal()); 31 | assert_eq!(10i8, MyEnum::Ten.ordinal()); 32 | 33 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 34 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 35 | assert_eq!(Some(MyEnum::Eight), MyEnum::from_ordinal(8i8)); 36 | assert_eq!(Some(MyEnum::Ten), MyEnum::from_ordinal(10i8)); 37 | 38 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 39 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 40 | assert_eq!(MyEnum::Eight, unsafe { MyEnum::from_ordinal_unsafe(8i8) }); 41 | assert_eq!(MyEnum::Ten, unsafe { MyEnum::from_ordinal_unsafe(10i8) }); 42 | } 43 | 44 | #[test] 45 | fn create_ordinalized_enum_5_2() { 46 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 47 | #[repr(i8)] 48 | enum MyEnum { 49 | Zero, 50 | One, 51 | Two = 1 + 1, 52 | Three, 53 | Four, 54 | Six = FOUR + 2, 55 | Seven, 56 | Eight, 57 | Ten = TEN as i8, 58 | Eleven, 59 | Twelve, 60 | Hundred = 100, 61 | HundredOne, 62 | HundredTwo, 63 | } 64 | 65 | assert_eq!(14, MyEnum::VARIANT_COUNT); 66 | assert_eq!( 67 | [ 68 | MyEnum::Zero, 69 | MyEnum::One, 70 | MyEnum::Two, 71 | MyEnum::Three, 72 | MyEnum::Four, 73 | MyEnum::Six, 74 | MyEnum::Seven, 75 | MyEnum::Eight, 76 | MyEnum::Ten, 77 | MyEnum::Eleven, 78 | MyEnum::Twelve, 79 | MyEnum::Hundred, 80 | MyEnum::HundredOne, 81 | MyEnum::HundredTwo 82 | ], 83 | MyEnum::VARIANTS 84 | ); 85 | assert_eq!( 86 | [0i8, 1i8, 2i8, 3i8, 4i8, 6i8, 7i8, 8i8, 10i8, 11i8, 12i8, 100i8, 101i8, 102i8], 87 | MyEnum::VALUES 88 | ); 89 | 90 | assert_eq!(0i8, MyEnum::Zero.ordinal()); 91 | assert_eq!(1i8, MyEnum::One.ordinal()); 92 | assert_eq!(4i8, MyEnum::Four.ordinal()); 93 | assert_eq!(8i8, MyEnum::Eight.ordinal()); 94 | assert_eq!(12i8, MyEnum::Twelve.ordinal()); 95 | assert_eq!(102i8, MyEnum::HundredTwo.ordinal()); 96 | 97 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8)); 98 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i8)); 99 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 100 | assert_eq!(Some(MyEnum::Eight), MyEnum::from_ordinal(8i8)); 101 | assert_eq!(Some(MyEnum::Twelve), MyEnum::from_ordinal(12i8)); 102 | assert_eq!(Some(MyEnum::HundredTwo), MyEnum::from_ordinal(102i8)); 103 | 104 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 105 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 106 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 107 | assert_eq!(MyEnum::Eight, unsafe { MyEnum::from_ordinal_unsafe(8i8) }); 108 | assert_eq!(MyEnum::Twelve, unsafe { MyEnum::from_ordinal_unsafe(12i8) }); 109 | assert_eq!(MyEnum::HundredTwo, unsafe { MyEnum::from_ordinal_unsafe(102i8) }); 110 | } 111 | -------------------------------------------------------------------------------- /enum-ordinalize-derive/src/int128.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cmp::Ordering, 3 | fmt::{self, Display, Formatter}, 4 | num::ParseIntError, 5 | ops::Neg, 6 | str::FromStr, 7 | }; 8 | 9 | #[derive(Debug, Copy, Eq, Clone)] 10 | pub(crate) enum Int128 { 11 | Signed(i128), 12 | Unsigned(u128), 13 | } 14 | 15 | impl PartialEq for Int128 { 16 | #[inline] 17 | fn eq(&self, other: &Int128) -> bool { 18 | match self { 19 | Self::Signed(i) => match other { 20 | Self::Signed(i2) => i.eq(i2), 21 | Self::Unsigned(u2) => { 22 | if i.is_negative() { 23 | false 24 | } else { 25 | (*i as u128).eq(u2) 26 | } 27 | }, 28 | }, 29 | Self::Unsigned(u) => match other { 30 | Self::Signed(i2) => { 31 | if i2.is_negative() { 32 | false 33 | } else { 34 | u.eq(&(*i2 as u128)) 35 | } 36 | }, 37 | Self::Unsigned(u2) => u.eq(u2), 38 | }, 39 | } 40 | } 41 | } 42 | 43 | impl PartialOrd for Int128 { 44 | #[inline] 45 | fn partial_cmp(&self, other: &Self) -> Option { 46 | Some(self.cmp(other)) 47 | } 48 | } 49 | 50 | impl Ord for Int128 { 51 | #[inline] 52 | fn cmp(&self, other: &Self) -> Ordering { 53 | match self { 54 | Self::Signed(i) => match other { 55 | Self::Signed(i2) => i.cmp(i2), 56 | Self::Unsigned(u2) => { 57 | if i.is_negative() { 58 | Ordering::Less 59 | } else { 60 | (*i as u128).cmp(u2) 61 | } 62 | }, 63 | }, 64 | Self::Unsigned(u) => match other { 65 | Self::Signed(i2) => { 66 | if i2.is_negative() { 67 | Ordering::Greater 68 | } else { 69 | u.cmp(&(*i2 as u128)) 70 | } 71 | }, 72 | Self::Unsigned(u2) => u.cmp(u2), 73 | }, 74 | } 75 | } 76 | } 77 | 78 | impl Display for Int128 { 79 | #[inline] 80 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 81 | match self { 82 | Self::Signed(i) => Display::fmt(i, f), 83 | Self::Unsigned(u) => Display::fmt(u, f), 84 | } 85 | } 86 | } 87 | 88 | impl Default for Int128 { 89 | #[inline] 90 | fn default() -> Self { 91 | Self::ZERO 92 | } 93 | } 94 | 95 | impl Int128 { 96 | pub(crate) const ZERO: Self = Self::Unsigned(0); 97 | } 98 | 99 | macro_rules! impl_from_signed { 100 | (@inner $t: ty) => { 101 | impl From<$t> for Int128 { 102 | #[inline] 103 | fn from(value: $t) -> Self { 104 | Int128::Signed(value as i128) 105 | } 106 | } 107 | }; 108 | ($($t: ty),+ $(,)*) => { 109 | $( 110 | impl_from_signed!(@inner $t); 111 | )* 112 | }; 113 | } 114 | 115 | impl_from_signed!(i8, i16, i32, i64, i128, isize); 116 | 117 | impl FromStr for Int128 { 118 | type Err = ParseIntError; 119 | 120 | #[inline] 121 | fn from_str(s: &str) -> Result { 122 | if s.starts_with('-') { 123 | Ok(Self::Signed(s.parse()?)) 124 | } else { 125 | Ok(Self::Unsigned(s.parse()?)) 126 | } 127 | } 128 | } 129 | 130 | impl Neg for Int128 { 131 | type Output = Int128; 132 | 133 | fn neg(self) -> Self::Output { 134 | match self { 135 | Self::Signed(i) => { 136 | if i == i128::MIN { 137 | Self::Unsigned(1 << 127) 138 | } else { 139 | Self::Signed(-i) 140 | } 141 | }, 142 | Self::Unsigned(u) => match u.cmp(&(1 << 127)) { 143 | Ordering::Equal => Self::Signed(i128::MIN), 144 | Ordering::Less => Self::Signed(-(u as i128)), 145 | Ordering::Greater => panic!("-{} is experiencing an overflow", u), 146 | }, 147 | } 148 | } 149 | } 150 | 151 | impl Int128 { 152 | #[inline] 153 | pub(crate) fn inc(&mut self) { 154 | match self { 155 | Self::Signed(i) => { 156 | if *i == i128::MAX { 157 | *self = Self::Unsigned(1 << 127) 158 | } else { 159 | *i += 1; 160 | } 161 | }, 162 | Self::Unsigned(u) => { 163 | *u = u.saturating_add(1); 164 | }, 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /enum-ordinalize/README.md: -------------------------------------------------------------------------------- 1 | Enum Ordinalize 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/enum-ordinalize/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/enum-ordinalize/actions/workflows/ci.yml) 5 | 6 | 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. 7 | 8 | ## Usage 9 | 10 | Use `#[derive(Ordinalize)]` to have an enum (which must only has unit variants) implement the `Ordinalize` trait. 11 | 12 | ```rust 13 | use enum_ordinalize::Ordinalize; 14 | 15 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 16 | enum MyEnum { 17 | Zero, 18 | One, 19 | Two, 20 | } 21 | 22 | assert_eq!(3, MyEnum::VARIANT_COUNT); 23 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two], MyEnum::VARIANTS); 24 | assert_eq!([0i8, 1i8, 2i8], MyEnum::VALUES); 25 | 26 | assert_eq!(0i8, MyEnum::Zero.ordinal()); 27 | assert_eq!(1i8, MyEnum::One.ordinal()); 28 | assert_eq!(2i8, MyEnum::Two.ordinal()); 29 | 30 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8)); 31 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i8)); 32 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 33 | 34 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 35 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 36 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 37 | ``` 38 | 39 | #### The (Ordinal) Size of an Enum 40 | 41 | The ordinal value is an integer whose size is determined by the enum itself. The size of the enum increases with the magnitude of the variants' values, whether larger (or smaller if negative). 42 | 43 | For example, 44 | 45 | ```rust 46 | use enum_ordinalize::Ordinalize; 47 | 48 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 49 | enum MyEnum { 50 | Zero, 51 | One, 52 | Two, 53 | Thousand = 1000, 54 | } 55 | 56 | assert_eq!(4, MyEnum::VARIANT_COUNT); 57 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS); 58 | assert_eq!([0i16, 1i16, 2i16, 1000i16], MyEnum::VALUES); 59 | 60 | assert_eq!(0i16, MyEnum::Zero.ordinal()); 61 | assert_eq!(1i16, MyEnum::One.ordinal()); 62 | assert_eq!(2i16, MyEnum::Two.ordinal()); 63 | 64 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i16)); 65 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i16)); 66 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i16)); 67 | 68 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i16) }); 69 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i16) }); 70 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i16) }); 71 | ``` 72 | 73 | In order to accommodate the value `1000`, the size of `MyEnum` increases. Consequently, the ordinal is represented in `i16` instead of `i8`. 74 | 75 | You can utilize the `#[repr(type)]` attribute to explicitly control the size. For instance, 76 | 77 | ```rust 78 | use enum_ordinalize::Ordinalize; 79 | 80 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 81 | #[repr(usize)] 82 | enum MyEnum { 83 | Zero, 84 | One, 85 | Two, 86 | Thousand = 1000, 87 | } 88 | 89 | assert_eq!(4, MyEnum::VARIANT_COUNT); 90 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS); 91 | assert_eq!([0usize, 1usize, 2usize, 1000usize], MyEnum::VALUES); 92 | 93 | assert_eq!(0usize, MyEnum::Zero.ordinal()); 94 | assert_eq!(1usize, MyEnum::One.ordinal()); 95 | assert_eq!(2usize, MyEnum::Two.ordinal()); 96 | 97 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0usize)); 98 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1usize)); 99 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2usize)); 100 | 101 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0usize) }); 102 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1usize) }); 103 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2usize) }); 104 | ``` 105 | 106 | #### Useful Increment 107 | 108 | The integers represented by variants can be extended in successive increments and set explicitly from any value. 109 | 110 | ```rust 111 | use enum_ordinalize::Ordinalize; 112 | 113 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 114 | enum MyEnum { 115 | Two = 2, 116 | Three, 117 | Four, 118 | Eight = 8, 119 | Nine, 120 | NegativeTen = -10, 121 | NegativeNine, 122 | } 123 | 124 | assert_eq!(7, MyEnum::VARIANT_COUNT); 125 | assert_eq!([MyEnum::Two, MyEnum::Three, MyEnum::Four, MyEnum::Eight, MyEnum::Nine, MyEnum::NegativeTen, MyEnum::NegativeNine], MyEnum::VARIANTS); 126 | assert_eq!([2i8, 3i8, 4i8, 8i8, 9i8, -10i8, -9i8], MyEnum::VALUES); 127 | 128 | assert_eq!(4i8, MyEnum::Four.ordinal()); 129 | assert_eq!(9i8, MyEnum::Nine.ordinal()); 130 | assert_eq!(-9i8, MyEnum::NegativeNine.ordinal()); 131 | 132 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 133 | assert_eq!(Some(MyEnum::Nine), MyEnum::from_ordinal(9i8)); 134 | assert_eq!(Some(MyEnum::NegativeNine), MyEnum::from_ordinal(-9i8)); 135 | 136 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 137 | assert_eq!(MyEnum::Nine, unsafe { MyEnum::from_ordinal_unsafe(9i8) }); 138 | assert_eq!(MyEnum::NegativeNine, unsafe { MyEnum::from_ordinal_unsafe(-9i8) }); 139 | ``` 140 | 141 | #### Implement Functionality for an enum on Itself 142 | 143 | For some reason, if you don't want to implement the `Ordinalize` trait for your enum, you can choose to disable the trait implementation and enable the constants/functions one by one. Functions are `const fn`. Names and visibility can also be defined by you. 144 | 145 | ```rust 146 | use enum_ordinalize::Ordinalize; 147 | 148 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 149 | #[ordinalize(impl_trait = false)] 150 | #[ordinalize(variant_count(pub const VARIANT_COUNT, doc = "The count of variants."))] 151 | #[ordinalize(variants(pub const VARIANTS, doc = "List of this enum's variants."))] 152 | #[ordinalize(values(pub const VALUES, doc = "List of values for all variants of this enum."))] 153 | #[ordinalize(ordinal(pub const fn ordinal, doc = "Retrieve the integer number of this variant."))] 154 | #[ordinalize(from_ordinal(pub const fn from_ordinal, doc = "Obtain a variant based on an integer number."))] 155 | #[ordinalize(from_ordinal_unsafe( 156 | pub const fn from_ordinal_unsafe, 157 | doc = "Obtain a variant based on an integer number.", 158 | doc = "# Safety", 159 | doc = "You have to ensure that the input integer number can correspond to a variant on your own.", 160 | ))] 161 | enum MyEnum { 162 | A, 163 | B, 164 | } 165 | 166 | assert_eq!(2, MyEnum::VARIANT_COUNT); 167 | assert_eq!([MyEnum::A, MyEnum::B], MyEnum::VARIANTS); 168 | assert_eq!([0i8, 1i8], MyEnum::VALUES); 169 | 170 | assert_eq!(0i8, MyEnum::A.ordinal()); 171 | assert_eq!(1i8, MyEnum::B.ordinal()); 172 | 173 | assert_eq!(Some(MyEnum::A), MyEnum::from_ordinal(0i8)); 174 | assert_eq!(Some(MyEnum::B), MyEnum::from_ordinal(1i8)); 175 | 176 | assert_eq!(MyEnum::A, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 177 | assert_eq!(MyEnum::B, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 178 | ``` 179 | 180 | ## Crates.io 181 | 182 | https://crates.io/crates/enum-ordinalize 183 | 184 | ## Documentation 185 | 186 | https://docs.rs/enum-ordinalize 187 | 188 | ## License 189 | 190 | [MIT](LICENSE) -------------------------------------------------------------------------------- /enum-ordinalize/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Enum Ordinalize 3 | 4 | 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. 5 | 6 | ## Usage 7 | 8 | Use `#[derive(Ordinalize)]` to have an enum (which must only has unit variants) implement the `Ordinalize` trait. 9 | 10 | ```rust 11 | # #[cfg(all(feature = "derive", feature = "traits"))] 12 | # { 13 | use enum_ordinalize::Ordinalize; 14 | 15 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 16 | enum MyEnum { 17 | Zero, 18 | One, 19 | Two, 20 | } 21 | 22 | assert_eq!(3, MyEnum::VARIANT_COUNT); 23 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two], MyEnum::VARIANTS); 24 | assert_eq!([0i8, 1i8, 2i8], MyEnum::VALUES); 25 | 26 | assert_eq!(0i8, MyEnum::Zero.ordinal()); 27 | assert_eq!(1i8, MyEnum::One.ordinal()); 28 | assert_eq!(2i8, MyEnum::Two.ordinal()); 29 | 30 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8)); 31 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i8)); 32 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 33 | 34 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 35 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 36 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 37 | # } 38 | ``` 39 | 40 | #### The (Ordinal) Size of an Enum 41 | 42 | The ordinal value is an integer whose size is determined by the enum itself. The size of the enum increases with the magnitude of the variants' values, whether larger (or smaller if negative). 43 | 44 | For example, 45 | 46 | ```rust 47 | # #[cfg(all(feature = "derive", feature = "traits"))] 48 | # { 49 | use enum_ordinalize::Ordinalize; 50 | 51 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 52 | enum MyEnum { 53 | Zero, 54 | One, 55 | Two, 56 | Thousand = 1000, 57 | } 58 | 59 | assert_eq!(4, MyEnum::VARIANT_COUNT); 60 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS); 61 | assert_eq!([0i16, 1i16, 2i16, 1000i16], MyEnum::VALUES); 62 | 63 | assert_eq!(0i16, MyEnum::Zero.ordinal()); 64 | assert_eq!(1i16, MyEnum::One.ordinal()); 65 | assert_eq!(2i16, MyEnum::Two.ordinal()); 66 | 67 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i16)); 68 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i16)); 69 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i16)); 70 | 71 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i16) }); 72 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i16) }); 73 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i16) }); 74 | # } 75 | ``` 76 | 77 | In order to accommodate the value `1000`, the size of `MyEnum` increases. Consequently, the ordinal is represented in `i16` instead of `i8`. 78 | 79 | You can utilize the `#[repr(type)]` attribute to explicitly control the size. For instance, 80 | 81 | ```rust 82 | # #[cfg(all(feature = "derive", feature = "traits"))] 83 | # { 84 | use enum_ordinalize::Ordinalize; 85 | 86 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 87 | #[repr(usize)] 88 | enum MyEnum { 89 | Zero, 90 | One, 91 | Two, 92 | Thousand = 1000, 93 | } 94 | 95 | assert_eq!(4, MyEnum::VARIANT_COUNT); 96 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS); 97 | assert_eq!([0usize, 1usize, 2usize, 1000usize], MyEnum::VALUES); 98 | 99 | assert_eq!(0usize, MyEnum::Zero.ordinal()); 100 | assert_eq!(1usize, MyEnum::One.ordinal()); 101 | assert_eq!(2usize, MyEnum::Two.ordinal()); 102 | 103 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0usize)); 104 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1usize)); 105 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2usize)); 106 | 107 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0usize) }); 108 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1usize) }); 109 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2usize) }); 110 | # } 111 | ``` 112 | 113 | #### Useful Increment 114 | 115 | The integers represented by variants can be extended in successive increments and set explicitly from any value. 116 | 117 | ```rust 118 | # #[cfg(all(feature = "derive", feature = "traits"))] 119 | # { 120 | use enum_ordinalize::Ordinalize; 121 | 122 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 123 | enum MyEnum { 124 | Two = 2, 125 | Three, 126 | Four, 127 | Eight = 8, 128 | Nine, 129 | NegativeTen = -10, 130 | NegativeNine, 131 | } 132 | 133 | assert_eq!(7, MyEnum::VARIANT_COUNT); 134 | assert_eq!([MyEnum::Two, MyEnum::Three, MyEnum::Four, MyEnum::Eight, MyEnum::Nine, MyEnum::NegativeTen, MyEnum::NegativeNine], MyEnum::VARIANTS); 135 | assert_eq!([2i8, 3i8, 4i8, 8i8, 9i8, -10i8, -9i8], MyEnum::VALUES); 136 | 137 | assert_eq!(4i8, MyEnum::Four.ordinal()); 138 | assert_eq!(9i8, MyEnum::Nine.ordinal()); 139 | assert_eq!(-9i8, MyEnum::NegativeNine.ordinal()); 140 | 141 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 142 | assert_eq!(Some(MyEnum::Nine), MyEnum::from_ordinal(9i8)); 143 | assert_eq!(Some(MyEnum::NegativeNine), MyEnum::from_ordinal(-9i8)); 144 | 145 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 146 | assert_eq!(MyEnum::Nine, unsafe { MyEnum::from_ordinal_unsafe(9i8) }); 147 | assert_eq!(MyEnum::NegativeNine, unsafe { MyEnum::from_ordinal_unsafe(-9i8) }); 148 | # } 149 | ``` 150 | 151 | #### Implement Functionality for an enum on Itself 152 | 153 | For some reason, if you don't want to implement the `Ordinalize` trait for your enum, you can choose to disable the trait implementation and enable the constants/functions one by one. Functions are `const fn`. Names and visibility can also be defined by you. 154 | 155 | ```rust 156 | # #[cfg(feature = "derive")] 157 | # { 158 | use enum_ordinalize::Ordinalize; 159 | 160 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 161 | #[ordinalize(impl_trait = false)] 162 | #[ordinalize(variant_count(pub const VARIANT_COUNT, doc = "The count of variants."))] 163 | #[ordinalize(variants(pub const VARIANTS, doc = "List of this enum's variants."))] 164 | #[ordinalize(values(pub const VALUES, doc = "List of values for all variants of this enum."))] 165 | #[ordinalize(ordinal(pub const fn ordinal, doc = "Retrieve the integer number of this variant."))] 166 | #[ordinalize(from_ordinal(pub const fn from_ordinal, doc = "Obtain a variant based on an integer number."))] 167 | #[ordinalize(from_ordinal_unsafe( 168 | pub const fn from_ordinal_unsafe, 169 | doc = "Obtain a variant based on an integer number.", 170 | doc = "# Safety", 171 | doc = "You have to ensure that the input integer number can correspond to a variant on your own.", 172 | ))] 173 | enum MyEnum { 174 | A, 175 | B, 176 | } 177 | 178 | assert_eq!(2, MyEnum::VARIANT_COUNT); 179 | assert_eq!([MyEnum::A, MyEnum::B], MyEnum::VARIANTS); 180 | assert_eq!([0i8, 1i8], MyEnum::VALUES); 181 | 182 | assert_eq!(0i8, MyEnum::A.ordinal()); 183 | assert_eq!(1i8, MyEnum::B.ordinal()); 184 | 185 | assert_eq!(Some(MyEnum::A), MyEnum::from_ordinal(0i8)); 186 | assert_eq!(Some(MyEnum::B), MyEnum::from_ordinal(1i8)); 187 | 188 | assert_eq!(MyEnum::A, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 189 | assert_eq!(MyEnum::B, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 190 | # } 191 | ``` 192 | */ 193 | 194 | #![no_std] 195 | #![cfg_attr(docsrs, feature(doc_cfg))] 196 | 197 | #[cfg(feature = "traits")] 198 | mod traits; 199 | 200 | #[cfg(feature = "derive")] 201 | pub use enum_ordinalize_derive::Ordinalize; 202 | #[cfg(feature = "traits")] 203 | pub use traits::Ordinalize; 204 | -------------------------------------------------------------------------------- /enum-ordinalize/tests/derive.rs: -------------------------------------------------------------------------------- 1 | #![cfg(all(feature = "derive", feature = "traits"))] 2 | 3 | use enum_ordinalize::Ordinalize; 4 | 5 | #[test] 6 | fn create_ordinalized_enum_1_1() { 7 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 8 | enum MyEnum { 9 | Zero, 10 | One, 11 | Two, 12 | } 13 | 14 | assert_eq!(3, MyEnum::VARIANT_COUNT); 15 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two], MyEnum::VARIANTS); 16 | assert_eq!([0i8, 1i8, 2i8], MyEnum::VALUES); 17 | 18 | assert_eq!(0i8, MyEnum::Zero.ordinal()); 19 | assert_eq!(1i8, MyEnum::One.ordinal()); 20 | assert_eq!(2i8, MyEnum::Two.ordinal()); 21 | 22 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8)); 23 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i8)); 24 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 25 | 26 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 27 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i8) }); 28 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 29 | } 30 | 31 | #[test] 32 | fn create_ordinalized_enum_1_2() { 33 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 34 | #[repr(u64)] 35 | enum MyEnum { 36 | Zero, 37 | One, 38 | Two, 39 | } 40 | 41 | assert_eq!(3, MyEnum::VARIANT_COUNT); 42 | assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two], MyEnum::VARIANTS); 43 | assert_eq!([0u64, 1u64, 2u64], MyEnum::VALUES); 44 | 45 | assert_eq!(0u64, MyEnum::Zero.ordinal()); 46 | assert_eq!(1u64, MyEnum::One.ordinal()); 47 | assert_eq!(2u64, MyEnum::Two.ordinal()); 48 | 49 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0u64)); 50 | assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1u64)); 51 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2u64)); 52 | 53 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0u64) }); 54 | assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1u64) }); 55 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2u64) }); 56 | } 57 | 58 | #[test] 59 | fn create_ordinalized_enum_2() { 60 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 61 | enum MyEnum { 62 | Two = 2, 63 | Four = 4, 64 | Eight = 8, 65 | } 66 | 67 | assert_eq!(3, MyEnum::VARIANT_COUNT); 68 | assert_eq!([MyEnum::Two, MyEnum::Four, MyEnum::Eight], MyEnum::VARIANTS); 69 | assert_eq!([2i8, 4i8, 8i8], MyEnum::VALUES); 70 | 71 | assert_eq!(2i8, MyEnum::Two.ordinal()); 72 | assert_eq!(4i8, MyEnum::Four.ordinal()); 73 | assert_eq!(8i8, MyEnum::Eight.ordinal()); 74 | 75 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 76 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 77 | assert_eq!(Some(MyEnum::Eight), MyEnum::from_ordinal(8i8)); 78 | 79 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 80 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 81 | assert_eq!(MyEnum::Eight, unsafe { MyEnum::from_ordinal_unsafe(8i8) }); 82 | } 83 | 84 | #[test] 85 | fn create_ordinalized_enum_3() { 86 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 87 | enum MyEnum { 88 | Two = 2, 89 | Three, 90 | Four, 91 | Ten = 10, 92 | Eleven = 11, 93 | } 94 | 95 | assert_eq!(5, MyEnum::VARIANT_COUNT); 96 | assert_eq!( 97 | [MyEnum::Two, MyEnum::Three, MyEnum::Four, MyEnum::Ten, MyEnum::Eleven], 98 | MyEnum::VARIANTS 99 | ); 100 | assert_eq!([2i8, 3i8, 4i8, 10i8, 11i8], MyEnum::VALUES); 101 | 102 | assert_eq!(2i8, MyEnum::Two.ordinal()); 103 | assert_eq!(3i8, MyEnum::Three.ordinal()); 104 | assert_eq!(4i8, MyEnum::Four.ordinal()); 105 | assert_eq!(10i8, MyEnum::Ten.ordinal()); 106 | assert_eq!(11i8, MyEnum::Eleven.ordinal()); 107 | 108 | assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8)); 109 | assert_eq!(Some(MyEnum::Three), MyEnum::from_ordinal(3i8)); 110 | assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8)); 111 | assert_eq!(Some(MyEnum::Ten), MyEnum::from_ordinal(10i8)); 112 | assert_eq!(Some(MyEnum::Eleven), MyEnum::from_ordinal(11i8)); 113 | 114 | assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) }); 115 | assert_eq!(MyEnum::Three, unsafe { MyEnum::from_ordinal_unsafe(3i8) }); 116 | assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) }); 117 | assert_eq!(MyEnum::Ten, unsafe { MyEnum::from_ordinal_unsafe(10i8) }); 118 | assert_eq!(MyEnum::Eleven, unsafe { MyEnum::from_ordinal_unsafe(11i8) }); 119 | } 120 | 121 | #[test] 122 | fn create_ordinalized_enum_4_1() { 123 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 124 | enum MyEnum { 125 | Zero, 126 | Thousand = 1000, 127 | ThousandZeroOne, 128 | } 129 | 130 | assert_eq!(3, MyEnum::VARIANT_COUNT); 131 | assert_eq!([MyEnum::Zero, MyEnum::Thousand, MyEnum::ThousandZeroOne], MyEnum::VARIANTS); 132 | assert_eq!([0i16, 1000i16, 1001i16], MyEnum::VALUES); 133 | 134 | assert_eq!(0i16, MyEnum::Zero.ordinal()); 135 | assert_eq!(1000i16, MyEnum::Thousand.ordinal()); 136 | assert_eq!(1001i16, MyEnum::ThousandZeroOne.ordinal()); 137 | 138 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i16)); 139 | assert_eq!(Some(MyEnum::Thousand), MyEnum::from_ordinal(1000i16)); 140 | assert_eq!(Some(MyEnum::ThousandZeroOne), MyEnum::from_ordinal(1001i16)); 141 | 142 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i16) }); 143 | assert_eq!(MyEnum::Thousand, unsafe { MyEnum::from_ordinal_unsafe(1000i16) }); 144 | assert_eq!(MyEnum::ThousandZeroOne, unsafe { MyEnum::from_ordinal_unsafe(1001i16) }); 145 | } 146 | 147 | #[test] 148 | fn create_ordinalized_enum_4_2() { 149 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 150 | enum MyEnum { 151 | Zero, 152 | NegativeThousand = -1000, 153 | NegativeNineHundredNinetyNine, 154 | } 155 | 156 | assert_eq!(3, MyEnum::VARIANT_COUNT); 157 | assert_eq!( 158 | [MyEnum::Zero, MyEnum::NegativeThousand, MyEnum::NegativeNineHundredNinetyNine], 159 | MyEnum::VARIANTS 160 | ); 161 | assert_eq!([0i16, -1000i16, -999i16], MyEnum::VALUES); 162 | 163 | assert_eq!(0i16, MyEnum::Zero.ordinal()); 164 | assert_eq!(-1000i16, MyEnum::NegativeThousand.ordinal()); 165 | assert_eq!(-999i16, MyEnum::NegativeNineHundredNinetyNine.ordinal()); 166 | 167 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i16)); 168 | assert_eq!(Some(MyEnum::NegativeThousand), MyEnum::from_ordinal(-1000i16)); 169 | assert_eq!(Some(MyEnum::NegativeNineHundredNinetyNine), MyEnum::from_ordinal(-999i16)); 170 | 171 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i16) }); 172 | assert_eq!(MyEnum::NegativeThousand, unsafe { MyEnum::from_ordinal_unsafe(-1000i16) }); 173 | assert_eq!(MyEnum::NegativeNineHundredNinetyNine, unsafe { 174 | MyEnum::from_ordinal_unsafe(-999i16) 175 | }); 176 | } 177 | 178 | #[test] 179 | fn create_ordinalized_enum_5() { 180 | #[derive(Debug, PartialEq, Eq, Ordinalize)] 181 | enum MyEnum { 182 | Zero, 183 | } 184 | 185 | assert_eq!(1, MyEnum::VARIANT_COUNT); 186 | assert_eq!([MyEnum::Zero], MyEnum::VARIANTS); 187 | assert_eq!([0i8], MyEnum::VALUES); 188 | 189 | assert_eq!(0i8, MyEnum::Zero.ordinal()); 190 | 191 | assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8)); 192 | 193 | assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) }); 194 | } 195 | -------------------------------------------------------------------------------- /enum-ordinalize-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # Enum Ordinalize Derive 3 | 4 | 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. 5 | */ 6 | 7 | #![no_std] 8 | 9 | #[macro_use] 10 | extern crate alloc; 11 | 12 | mod int128; 13 | mod int_wrapper; 14 | mod panic; 15 | mod variant_type; 16 | 17 | use alloc::{string::ToString, vec::Vec}; 18 | 19 | use proc_macro::TokenStream; 20 | use quote::quote; 21 | use syn::{ 22 | parse::{Parse, ParseStream}, 23 | parse_macro_input, 24 | punctuated::Punctuated, 25 | spanned::Spanned, 26 | Data, DeriveInput, Expr, Fields, Ident, Lit, Meta, Token, UnOp, Visibility, 27 | }; 28 | use variant_type::VariantType; 29 | 30 | use crate::{int128::Int128, int_wrapper::IntWrapper}; 31 | 32 | #[proc_macro_derive(Ordinalize, attributes(ordinalize))] 33 | pub fn ordinalize_derive(input: TokenStream) -> TokenStream { 34 | struct ConstMember { 35 | vis: Option, 36 | ident: Ident, 37 | meta: Vec, 38 | function: bool, 39 | } 40 | 41 | impl Parse for ConstMember { 42 | #[inline] 43 | fn parse(input: ParseStream) -> syn::Result { 44 | let vis = input.parse::().ok(); 45 | 46 | let _ = input.parse::(); 47 | 48 | let function = input.parse::().is_ok(); 49 | 50 | let ident = input.parse::()?; 51 | 52 | let mut meta = Vec::new(); 53 | 54 | if !input.is_empty() { 55 | input.parse::()?; 56 | 57 | if !input.is_empty() { 58 | let result = Punctuated::::parse_terminated(input)?; 59 | 60 | let mut has_inline = false; 61 | 62 | for m in result { 63 | if m.path().is_ident("inline") { 64 | has_inline = true; 65 | } 66 | 67 | meta.push(m); 68 | } 69 | 70 | if !has_inline { 71 | meta.push(syn::parse_str("inline")?); 72 | } 73 | } 74 | } 75 | 76 | Ok(Self { 77 | vis, 78 | ident, 79 | meta, 80 | function, 81 | }) 82 | } 83 | } 84 | 85 | struct ConstFunctionMember { 86 | vis: Option, 87 | ident: Ident, 88 | meta: Vec, 89 | } 90 | 91 | impl Parse for ConstFunctionMember { 92 | #[inline] 93 | fn parse(input: ParseStream) -> syn::Result { 94 | let vis = input.parse::().ok(); 95 | 96 | let _ = input.parse::(); 97 | 98 | input.parse::()?; 99 | 100 | let ident = input.parse::()?; 101 | 102 | let mut meta = Vec::new(); 103 | 104 | if !input.is_empty() { 105 | input.parse::()?; 106 | 107 | if !input.is_empty() { 108 | let result = Punctuated::::parse_terminated(input)?; 109 | 110 | let mut has_inline = false; 111 | 112 | for m in result { 113 | if m.path().is_ident("inline") { 114 | has_inline = true; 115 | } 116 | 117 | meta.push(m); 118 | } 119 | 120 | if !has_inline { 121 | meta.push(syn::parse_str("inline")?); 122 | } 123 | } 124 | } 125 | 126 | Ok(Self { 127 | vis, 128 | ident, 129 | meta, 130 | }) 131 | } 132 | } 133 | 134 | struct MyDeriveInput { 135 | ast: DeriveInput, 136 | variant_type: VariantType, 137 | values: Vec, 138 | variant_idents: Vec, 139 | use_constant_counter: bool, 140 | enable_trait: bool, 141 | enable_variant_count: Option, 142 | enable_variants: Option, 143 | enable_values: Option, 144 | enable_from_ordinal_unsafe: Option, 145 | enable_from_ordinal: Option, 146 | enable_ordinal: Option, 147 | } 148 | 149 | impl Parse for MyDeriveInput { 150 | fn parse(input: ParseStream) -> syn::Result { 151 | let ast = input.parse::()?; 152 | 153 | let mut variant_type = VariantType::default(); 154 | let mut enable_trait = cfg!(feature = "traits"); 155 | let mut enable_variant_count = None; 156 | let mut enable_variants = None; 157 | let mut enable_values = None; 158 | let mut enable_from_ordinal_unsafe = None; 159 | let mut enable_from_ordinal = None; 160 | let mut enable_ordinal = None; 161 | 162 | for attr in ast.attrs.iter() { 163 | let path = attr.path(); 164 | 165 | if let Some(ident) = path.get_ident() { 166 | match ident.to_string().as_str() { 167 | "repr" => { 168 | // #[repr(u8)], #[repr(u16)], ..., etc. 169 | if let Meta::List(list) = &attr.meta { 170 | let result = list.parse_args_with( 171 | Punctuated::::parse_terminated, 172 | )?; 173 | 174 | if let Some(value) = result.into_iter().next() { 175 | variant_type = VariantType::from_str(value.to_string()); 176 | } 177 | } 178 | 179 | break; 180 | }, 181 | "ordinalize" => { 182 | if let Meta::List(list) = &attr.meta { 183 | let result = list.parse_args_with( 184 | Punctuated::::parse_terminated, 185 | )?; 186 | 187 | for meta in result { 188 | let path = meta.path(); 189 | 190 | if let Some(ident) = path.get_ident() { 191 | match ident.to_string().as_str() { 192 | "impl_trait" => { 193 | if let Meta::NameValue(meta) = &meta { 194 | if let Expr::Lit(lit) = &meta.value { 195 | if let Lit::Bool(value) = &lit.lit { 196 | if cfg!(feature = "traits") { 197 | enable_trait = value.value; 198 | } 199 | } else { 200 | return Err( 201 | panic::bool_attribute_usage( 202 | ident, 203 | ident.span(), 204 | ), 205 | ); 206 | } 207 | } else { 208 | return Err(panic::bool_attribute_usage( 209 | ident, 210 | ident.span(), 211 | )); 212 | } 213 | } else { 214 | return Err(panic::bool_attribute_usage( 215 | ident, 216 | ident.span(), 217 | )); 218 | } 219 | }, 220 | "variant_count" => { 221 | if let Meta::List(list) = &meta { 222 | enable_variant_count = Some(list.parse_args()?); 223 | } else { 224 | return Err(panic::list_attribute_usage( 225 | ident, 226 | ident.span(), 227 | )); 228 | } 229 | }, 230 | "variants" => { 231 | if let Meta::List(list) = &meta { 232 | enable_variants = Some(list.parse_args()?); 233 | } else { 234 | return Err(panic::list_attribute_usage( 235 | ident, 236 | ident.span(), 237 | )); 238 | } 239 | }, 240 | "values" => { 241 | if let Meta::List(list) = &meta { 242 | enable_values = Some(list.parse_args()?); 243 | } else { 244 | return Err(panic::list_attribute_usage( 245 | ident, 246 | ident.span(), 247 | )); 248 | } 249 | }, 250 | "from_ordinal_unsafe" => { 251 | if let Meta::List(list) = &meta { 252 | enable_from_ordinal_unsafe = 253 | Some(list.parse_args()?); 254 | } else { 255 | return Err(panic::list_attribute_usage( 256 | ident, 257 | ident.span(), 258 | )); 259 | } 260 | }, 261 | "from_ordinal" => { 262 | if let Meta::List(list) = &meta { 263 | enable_from_ordinal = Some(list.parse_args()?); 264 | } else { 265 | return Err(panic::list_attribute_usage( 266 | ident, 267 | ident.span(), 268 | )); 269 | } 270 | }, 271 | "ordinal" => { 272 | if let Meta::List(list) = &meta { 273 | enable_ordinal = Some(list.parse_args()?); 274 | } else { 275 | return Err(panic::list_attribute_usage( 276 | ident, 277 | ident.span(), 278 | )); 279 | } 280 | }, 281 | _ => { 282 | return Err(panic::sub_attributes_for_ordinalize( 283 | ident.span(), 284 | )); 285 | }, 286 | } 287 | } else { 288 | return Err(panic::list_attribute_usage( 289 | ident, 290 | ident.span(), 291 | )); 292 | } 293 | } 294 | } else { 295 | return Err(panic::list_attribute_usage(ident, ident.span())); 296 | } 297 | }, 298 | _ => (), 299 | } 300 | } 301 | } 302 | 303 | let name = &ast.ident; 304 | 305 | if let Data::Enum(data) = &ast.data { 306 | let variant_count = data.variants.len(); 307 | 308 | if variant_count == 0 { 309 | return Err(panic::no_variant(name.span())); 310 | } 311 | 312 | let mut values: Vec = Vec::with_capacity(variant_count); 313 | let mut variant_idents: Vec = Vec::with_capacity(variant_count); 314 | 315 | let mut use_constant_counter = false; 316 | 317 | if let VariantType::NonDetermined = variant_type { 318 | let mut min = i128::MAX; 319 | let mut max = i128::MIN; 320 | let mut counter = 0; 321 | 322 | for variant in data.variants.iter() { 323 | if let Fields::Unit = variant.fields { 324 | if let Some((_, exp)) = variant.discriminant.as_ref() { 325 | match exp { 326 | Expr::Lit(lit) => { 327 | if let Lit::Int(lit) = &lit.lit { 328 | counter = lit.base10_parse().map_err(|error| { 329 | syn::Error::new(lit.span(), error) 330 | })?; 331 | } else { 332 | return Err(panic::unsupported_discriminant( 333 | lit.span(), 334 | )); 335 | } 336 | }, 337 | Expr::Unary(unary) => { 338 | if let UnOp::Neg(_) = unary.op { 339 | match unary.expr.as_ref() { 340 | Expr::Lit(lit) => { 341 | if let Lit::Int(lit) = &lit.lit { 342 | match lit.base10_parse::() { 343 | Ok(i) => { 344 | counter = -i; 345 | }, 346 | Err(error) => { 347 | // overflow 348 | if lit.base10_digits() == "170141183460469231731687303715884105728" { 349 | counter = i128::MIN; 350 | } else { 351 | return Err(syn::Error::new(lit.span(), error)); 352 | } 353 | }, 354 | } 355 | } else { 356 | return Err(panic::unsupported_discriminant(lit.span())); 357 | } 358 | }, 359 | Expr::Path(_) 360 | | Expr::Cast(_) 361 | | Expr::Binary(_) 362 | | Expr::Call(_) => { 363 | return Err(panic::constant_variable_on_non_determined_size_enum(unary.expr.span())) 364 | }, 365 | _ => return Err(panic::unsupported_discriminant(unary.expr.span())), 366 | } 367 | } else { 368 | return Err(panic::unsupported_discriminant( 369 | unary.op.span(), 370 | )); 371 | } 372 | }, 373 | Expr::Path(_) 374 | | Expr::Cast(_) 375 | | Expr::Binary(_) 376 | | Expr::Call(_) => { 377 | return Err( 378 | panic::constant_variable_on_non_determined_size_enum( 379 | exp.span(), 380 | ), 381 | ) 382 | }, 383 | _ => return Err(panic::unsupported_discriminant(exp.span())), 384 | } 385 | }; 386 | 387 | if min > counter { 388 | min = counter; 389 | } 390 | 391 | if max < counter { 392 | max = counter; 393 | } 394 | 395 | variant_idents.push(variant.ident.clone()); 396 | 397 | values.push(IntWrapper::from(counter)); 398 | 399 | counter = counter.saturating_add(1); 400 | } else { 401 | return Err(panic::not_unit_variant(variant.span())); 402 | } 403 | } 404 | 405 | if min >= i8::MIN as i128 && max <= i8::MAX as i128 { 406 | variant_type = VariantType::I8; 407 | } else if min >= i16::MIN as i128 && max <= i16::MAX as i128 { 408 | variant_type = VariantType::I16; 409 | } else if min >= i32::MIN as i128 && max <= i32::MAX as i128 { 410 | variant_type = VariantType::I32; 411 | } else if min >= i64::MIN as i128 && max <= i64::MAX as i128 { 412 | variant_type = VariantType::I64; 413 | } else { 414 | variant_type = VariantType::I128; 415 | } 416 | } else { 417 | let mut counter = Int128::ZERO; 418 | let mut constant_counter = 0; 419 | let mut last_exp: Option<&Expr> = None; 420 | 421 | for variant in data.variants.iter() { 422 | if let Fields::Unit = variant.fields { 423 | if let Some((_, exp)) = variant.discriminant.as_ref() { 424 | match exp { 425 | Expr::Lit(lit) => { 426 | if let Lit::Int(lit) = &lit.lit { 427 | counter = lit.base10_parse().map_err(|error| { 428 | syn::Error::new(lit.span(), error) 429 | })?; 430 | 431 | values.push(IntWrapper::from(counter)); 432 | 433 | counter.inc(); 434 | 435 | last_exp = None; 436 | } else { 437 | return Err(panic::unsupported_discriminant( 438 | lit.span(), 439 | )); 440 | } 441 | }, 442 | Expr::Unary(unary) => { 443 | if let UnOp::Neg(_) = unary.op { 444 | match unary.expr.as_ref() { 445 | Expr::Lit(lit) => { 446 | if let Lit::Int(lit) = &lit.lit { 447 | counter = -lit.base10_parse().map_err( 448 | |error| { 449 | syn::Error::new(lit.span(), error) 450 | }, 451 | )?; 452 | 453 | values.push(IntWrapper::from(counter)); 454 | 455 | counter.inc(); 456 | 457 | last_exp = None; 458 | } else { 459 | return Err( 460 | panic::unsupported_discriminant( 461 | lit.span(), 462 | ), 463 | ); 464 | } 465 | }, 466 | Expr::Path(_) => { 467 | values.push(IntWrapper::from((exp, 0))); 468 | 469 | last_exp = Some(exp); 470 | constant_counter = 1; 471 | }, 472 | Expr::Cast(_) | Expr::Binary(_) | Expr::Call(_) => { 473 | values.push(IntWrapper::from((exp, 0))); 474 | 475 | last_exp = Some(exp); 476 | constant_counter = 1; 477 | 478 | use_constant_counter = true; 479 | }, 480 | _ => { 481 | return Err(panic::unsupported_discriminant( 482 | exp.span(), 483 | )); 484 | }, 485 | } 486 | } else { 487 | return Err(panic::unsupported_discriminant( 488 | unary.op.span(), 489 | )); 490 | } 491 | }, 492 | Expr::Path(_) => { 493 | values.push(IntWrapper::from((exp, 0))); 494 | 495 | last_exp = Some(exp); 496 | constant_counter = 1; 497 | }, 498 | Expr::Cast(_) | Expr::Binary(_) | Expr::Call(_) => { 499 | values.push(IntWrapper::from((exp, 0))); 500 | 501 | last_exp = Some(exp); 502 | constant_counter = 1; 503 | 504 | use_constant_counter = true; 505 | }, 506 | _ => return Err(panic::unsupported_discriminant(exp.span())), 507 | } 508 | } else if let Some(exp) = last_exp { 509 | values.push(IntWrapper::from((exp, constant_counter))); 510 | 511 | constant_counter += 1; 512 | 513 | use_constant_counter = true; 514 | } else { 515 | values.push(IntWrapper::from(counter)); 516 | 517 | counter.inc(); 518 | } 519 | 520 | variant_idents.push(variant.ident.clone()); 521 | } else { 522 | return Err(panic::not_unit_variant(variant.span())); 523 | } 524 | } 525 | } 526 | 527 | Ok(MyDeriveInput { 528 | ast, 529 | variant_type, 530 | values, 531 | variant_idents, 532 | use_constant_counter, 533 | enable_trait, 534 | enable_variant_count, 535 | enable_variants, 536 | enable_values, 537 | enable_from_ordinal_unsafe, 538 | enable_from_ordinal, 539 | enable_ordinal, 540 | }) 541 | } else { 542 | Err(panic::not_enum(ast.ident.span())) 543 | } 544 | } 545 | } 546 | 547 | // Parse the token stream 548 | let derive_input = parse_macro_input!(input as MyDeriveInput); 549 | 550 | let MyDeriveInput { 551 | ast, 552 | variant_type, 553 | values, 554 | variant_idents, 555 | use_constant_counter, 556 | enable_trait, 557 | enable_variant_count, 558 | enable_variants, 559 | enable_values, 560 | enable_ordinal, 561 | enable_from_ordinal_unsafe, 562 | enable_from_ordinal, 563 | } = derive_input; 564 | 565 | // Get the identifier of the type. 566 | let name = &ast.ident; 567 | 568 | let variant_count = values.len(); 569 | 570 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 571 | 572 | // Build the code 573 | let mut expanded = proc_macro2::TokenStream::new(); 574 | 575 | if enable_trait { 576 | #[cfg(feature = "traits")] 577 | { 578 | let from_ordinal_unsafe = if variant_count == 1 { 579 | let variant_ident = &variant_idents[0]; 580 | 581 | quote! { 582 | #[inline] 583 | unsafe fn from_ordinal_unsafe(_number: #variant_type) -> Self { 584 | Self::#variant_ident 585 | } 586 | } 587 | } else { 588 | quote! { 589 | #[inline] 590 | unsafe fn from_ordinal_unsafe(number: #variant_type) -> Self { 591 | ::core::mem::transmute(number) 592 | } 593 | } 594 | }; 595 | 596 | let from_ordinal = if use_constant_counter { 597 | quote! { 598 | #[inline] 599 | fn from_ordinal(number: #variant_type) -> Option { 600 | if false { 601 | unreachable!() 602 | } #( else if number == #values { 603 | Some(Self::#variant_idents) 604 | } )* else { 605 | None 606 | } 607 | } 608 | } 609 | } else { 610 | quote! { 611 | #[inline] 612 | fn from_ordinal(number: #variant_type) -> Option { 613 | match number{ 614 | #( 615 | #values => Some(Self::#variant_idents), 616 | )* 617 | _ => None 618 | } 619 | } 620 | } 621 | }; 622 | 623 | expanded.extend(quote! { 624 | impl #impl_generics Ordinalize for #name #ty_generics #where_clause { 625 | type VariantType = #variant_type; 626 | 627 | const VARIANT_COUNT: usize = #variant_count; 628 | 629 | const VARIANTS: &'static [Self] = &[#( Self::#variant_idents, )*]; 630 | 631 | const VALUES: &'static [#variant_type] = &[#( #values, )*]; 632 | 633 | #[inline] 634 | fn ordinal(&self) -> #variant_type { 635 | match self { 636 | #( 637 | Self::#variant_idents => #values, 638 | )* 639 | } 640 | } 641 | 642 | #from_ordinal_unsafe 643 | 644 | #from_ordinal 645 | } 646 | }); 647 | } 648 | } 649 | 650 | let mut expanded_2 = proc_macro2::TokenStream::new(); 651 | 652 | if let Some(ConstMember { 653 | vis, 654 | ident, 655 | meta, 656 | function, 657 | }) = enable_variant_count 658 | { 659 | expanded_2.extend(if function { 660 | quote! { 661 | #(#[#meta])* 662 | #vis const fn #ident () -> usize { 663 | #variant_count 664 | } 665 | } 666 | } else { 667 | quote! { 668 | #(#[#meta])* 669 | #vis const #ident: usize = #variant_count; 670 | } 671 | }); 672 | } 673 | 674 | if let Some(ConstMember { 675 | vis, 676 | ident, 677 | meta, 678 | function, 679 | }) = enable_variants 680 | { 681 | expanded_2.extend(if function { 682 | quote! { 683 | #(#[#meta])* 684 | #vis const fn #ident () -> [Self; #variant_count] { 685 | [#( Self::#variant_idents, )*] 686 | } 687 | } 688 | } else { 689 | quote! { 690 | #(#[#meta])* 691 | #vis const #ident: [Self; #variant_count] = [#( Self::#variant_idents, )*]; 692 | } 693 | }); 694 | } 695 | 696 | if let Some(ConstMember { 697 | vis, 698 | ident, 699 | meta, 700 | function, 701 | }) = enable_values 702 | { 703 | expanded_2.extend(if function { 704 | quote! { 705 | #(#[#meta])* 706 | #vis const fn #ident () -> [#variant_type; #variant_count] { 707 | [#( #values, )*] 708 | } 709 | } 710 | } else { 711 | quote! { 712 | #(#[#meta])* 713 | #vis const #ident: [#variant_type; #variant_count] = [#( #values, )*]; 714 | } 715 | }); 716 | } 717 | 718 | if let Some(ConstFunctionMember { 719 | vis, 720 | ident, 721 | meta, 722 | }) = enable_from_ordinal_unsafe 723 | { 724 | let from_ordinal_unsafe = if variant_count == 1 { 725 | let variant_ident = &variant_idents[0]; 726 | 727 | quote! { 728 | #(#[#meta])* 729 | #vis const unsafe fn #ident (_number: #variant_type) -> Self { 730 | Self::#variant_ident 731 | } 732 | } 733 | } else { 734 | quote! { 735 | #(#[#meta])* 736 | #vis const unsafe fn #ident (number: #variant_type) -> Self { 737 | ::core::mem::transmute(number) 738 | } 739 | } 740 | }; 741 | 742 | expanded_2.extend(from_ordinal_unsafe); 743 | } 744 | 745 | if let Some(ConstFunctionMember { 746 | vis, 747 | ident, 748 | meta, 749 | }) = enable_from_ordinal 750 | { 751 | let from_ordinal = if use_constant_counter { 752 | quote! { 753 | #(#[#meta])* 754 | #vis const fn #ident (number: #variant_type) -> Option { 755 | if false { 756 | unreachable!() 757 | } #( else if number == #values { 758 | Some(Self::#variant_idents) 759 | } )* else { 760 | None 761 | } 762 | } 763 | } 764 | } else { 765 | quote! { 766 | #(#[#meta])* 767 | #vis const fn #ident (number: #variant_type) -> Option { 768 | match number{ 769 | #( 770 | #values => Some(Self::#variant_idents), 771 | )* 772 | _ => None 773 | } 774 | } 775 | } 776 | }; 777 | 778 | expanded_2.extend(from_ordinal); 779 | } 780 | 781 | if let Some(ConstFunctionMember { 782 | vis, 783 | ident, 784 | meta, 785 | }) = enable_ordinal 786 | { 787 | expanded_2.extend(quote! { 788 | #(#[#meta])* 789 | #vis const fn #ident (&self) -> #variant_type { 790 | match self { 791 | #( 792 | Self::#variant_idents => #values, 793 | )* 794 | } 795 | } 796 | }); 797 | } 798 | 799 | if !expanded_2.is_empty() { 800 | expanded.extend(quote! { 801 | impl #impl_generics #name #ty_generics #where_clause { 802 | #expanded_2 803 | } 804 | }); 805 | } 806 | 807 | expanded.into() 808 | } 809 | --------------------------------------------------------------------------------