enumset_derive-0.10.0/.cargo_vcs_info.json0000644000000001540000000000100141300ustar { "git": { "sha1": "6a9a67676e6ce885a3d3c68f5946d8a5b4a9db7f" }, "path_in_vcs": "enumset_derive" }enumset_derive-0.10.0/Cargo.toml0000644000000023530000000000100121310ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "enumset_derive" version = "0.10.0" authors = ["Alissa Rao "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "An internal helper crate for enumset. Not public API." documentation = "https://lymia.moe/doc/enumset/enumset/" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/Lymia/enumset" [lib] name = "enumset_derive" path = "src/lib.rs" proc-macro = true [dependencies.darling] version = "0.20" default-features = false [dependencies.proc-macro-crate] version = "3" optional = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "2" [features] serde = [] std_deprecation_warning = [] enumset_derive-0.10.0/Cargo.toml.orig000064400000000000000000000010661046102023000156120ustar 00000000000000[package] name = "enumset_derive" version = "0.10.0" authors = ["Alissa Rao "] edition = "2021" description = "An internal helper crate for enumset. Not public API." documentation = "https://lymia.moe/doc/enumset/enumset/" repository = "https://github.com/Lymia/enumset" license = "MIT/Apache-2.0" [lib] proc-macro = true [features] serde = [] std_deprecation_warning = [] [dependencies] darling = { version = "0.20", default-features = false } syn = "2" quote = "1" proc-macro2 = "1" proc-macro-crate = { version = "3", optional = true } enumset_derive-0.10.0/LICENSE-APACHE000064400000000000000000000251371046102023000146540ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. enumset_derive-0.10.0/LICENSE-MIT000064400000000000000000000020721046102023000143550ustar 00000000000000Copyright (c) 2017-2023 Alissa Rao Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. enumset_derive-0.10.0/README.md000064400000000000000000000001321046102023000141730ustar 00000000000000An internal helper crate for [enumset](https://github.com/Lymia/enumset). Not public API. enumset_derive-0.10.0/src/lib.rs000064400000000000000000001206441046102023000146320ustar 00000000000000#![recursion_limit = "256"] extern crate proc_macro; use darling::util::SpannedValue; use darling::*; use proc_macro::TokenStream; use proc_macro2::{Literal, Span, TokenStream as SynTokenStream}; use quote::*; use std::{collections::HashSet, fmt::Display}; use syn::spanned::Spanned; use syn::{Error, Result, *}; /// Helper function for emitting compile errors. fn error(span: Span, message: impl Display) -> Result { Err(Error::new(span, message)) } /// Decodes the custom attributes for our custom derive. #[derive(FromDeriveInput, Default)] #[darling(attributes(enumset), default)] struct EnumsetAttrs { no_ops: bool, no_super_impls: bool, #[darling(default)] repr: SpannedValue>, #[darling(default)] serialize_repr: SpannedValue>, serialize_deny_unknown: bool, #[darling(default)] crate_name: Option, // legacy options serialize_as_list: SpannedValue, // replaced with serialize_repr serialize_as_map: SpannedValue, // replaced with serialize_repr } /// The internal representation of an enumset. #[derive(Copy, Clone)] enum InternalRepr { /// internal repr: `u8` U8, /// internal repr: `u16` U16, /// internal repr: `u32` U32, /// internal repr: `u64` U64, /// internal repr: `u128` U128, /// internal repr: `[u64; size]` Array(usize), } impl InternalRepr { /// Determines the number of variants supported by this repr. fn supported_variants(&self) -> usize { match self { InternalRepr::U8 => 8, InternalRepr::U16 => 16, InternalRepr::U32 => 32, InternalRepr::U64 => 64, InternalRepr::U128 => 128, InternalRepr::Array(size) => size * 64, } } } /// The serde representation of the enumset. #[derive(Copy, Clone)] enum SerdeRepr { /// serde type: `u8` U8, /// serde type: `u16` U16, /// serde type: `u32` U32, /// serde type: `u64` U64, /// serde type: `u128` U128, /// serde type: list of `T` List, /// serde type: map of `T` to `bool` Map, /// serde type: list of `u64` Array, } impl SerdeRepr { /// Determines the number of variants supported by this repr. fn supported_variants(&self) -> Option { match self { SerdeRepr::U8 => Some(8), SerdeRepr::U16 => Some(16), SerdeRepr::U32 => Some(32), SerdeRepr::U64 => Some(64), SerdeRepr::U128 => Some(128), SerdeRepr::List => None, SerdeRepr::Map => None, SerdeRepr::Array => None, } } } /// An variant in the enum set type. struct EnumSetValue { /// The name of the variant. name: Ident, /// The discriminant of the variant. variant_repr: u32, } /// Stores information about the enum set type. #[allow(dead_code)] struct EnumSetInfo { /// The name of the enum. name: Ident, /// The crate name to use. crate_name: Option, /// The numeric type to represent the `EnumSet` as in memory. explicit_internal_repr: Option, /// Forces the internal numeric type of the `EnumSet` to be an array. internal_repr_force_array: bool, /// The numeric type to serialize the enum as. explicit_serde_repr: Option, /// A list of variants in the enum. variants: Vec, /// Visbility vis: Visibility, /// The highest encountered variant discriminant. max_discrim: u32, /// The span of the highest encountered variant. max_discrim_span: Option, /// The current variant discriminant. Used to track, e.g. `A=10,B,C`. cur_discrim: u32, /// A list of variant names that are already in use. used_variant_names: HashSet, /// A list of variant discriminants that are already in use. used_discriminants: HashSet, /// Avoid generating operator overloads on the enum type. no_ops: bool, /// Avoid generating implementations for `Clone`, `Copy`, `Eq`, and `PartialEq`. no_super_impls: bool, /// Disallow unknown bits while deserializing the enum. serialize_deny_unknown: bool, } impl EnumSetInfo { fn new(input: &DeriveInput, attrs: &EnumsetAttrs) -> EnumSetInfo { EnumSetInfo { name: input.ident.clone(), crate_name: attrs .crate_name .as_ref() .map(|x| Ident::new(x, Span::call_site())), explicit_internal_repr: None, internal_repr_force_array: false, explicit_serde_repr: None, variants: Vec::new(), vis: input.vis.clone(), max_discrim: 0, max_discrim_span: None, cur_discrim: 0, used_variant_names: HashSet::new(), used_discriminants: HashSet::new(), no_ops: attrs.no_ops, no_super_impls: attrs.no_super_impls, serialize_deny_unknown: attrs.serialize_deny_unknown, } } /// Explicits sets the serde representation of the enumset from a string. fn push_serialize_repr(&mut self, span: Span, ty: &str) -> Result<()> { match ty { "u8" => self.explicit_serde_repr = Some(SerdeRepr::U8), "u16" => self.explicit_serde_repr = Some(SerdeRepr::U16), "u32" => self.explicit_serde_repr = Some(SerdeRepr::U32), "u64" => self.explicit_serde_repr = Some(SerdeRepr::U64), "u128" => self.explicit_serde_repr = Some(SerdeRepr::U128), "list" => self.explicit_serde_repr = Some(SerdeRepr::List), "map" => self.explicit_serde_repr = Some(SerdeRepr::Map), "array" => self.explicit_serde_repr = Some(SerdeRepr::Array), _ => error(span, format!("`{}` is not a valid serialized representation.", ty))?, } Ok(()) } /// Explicitly sets the representation of the enumset from a string. fn push_repr(&mut self, span: Span, ty: &str) -> Result<()> { match ty { "u8" => self.explicit_internal_repr = Some(InternalRepr::U8), "u16" => self.explicit_internal_repr = Some(InternalRepr::U16), "u32" => self.explicit_internal_repr = Some(InternalRepr::U32), "u64" => self.explicit_internal_repr = Some(InternalRepr::U64), "u128" => self.explicit_internal_repr = Some(InternalRepr::U128), "array" => self.internal_repr_force_array = true, _ => error(span, format!("`{}` is not a valid internal enumset representation.", ty))?, } Ok(()) } /// Adds a variant to the enumset. fn push_variant(&mut self, variant: &Variant) -> Result<()> { if self.used_variant_names.contains(&variant.ident.to_string()) { error(variant.span(), "Duplicated variant name.") } else if let Fields::Unit = variant.fields { // Parse the discriminant. if let Some((_, expr)) = &variant.discriminant { if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = expr { match i.base10_parse() { Ok(val) => self.cur_discrim = val, Err(_) => error(expr.span(), "Enum discriminants must fit into `u32`.")?, } } else if let Expr::Unary(ExprUnary { op: UnOp::Neg(_), .. }) = expr { error(expr.span(), "Enum discriminants must not be negative.")?; } else { error(variant.span(), "Enum discriminants must be literal expressions.")?; } } // Validate the discriminant. let discriminant = self.cur_discrim; if discriminant >= 0xFFFFFFC0 { error(variant.span(), "Maximum discriminant allowed is `0xFFFFFFBF`.")?; } if self.used_discriminants.contains(&discriminant) { error(variant.span(), "Duplicated enum discriminant.")?; } // Add the variant to the info. self.cur_discrim += 1; if discriminant > self.max_discrim { self.max_discrim = discriminant; self.max_discrim_span = Some(variant.span()); } self.variants .push(EnumSetValue { name: variant.ident.clone(), variant_repr: discriminant }); self.used_variant_names.insert(variant.ident.to_string()); self.used_discriminants.insert(discriminant); Ok(()) } else { error(variant.span(), "`#[derive(EnumSetType)]` can only be used on fieldless enums.") } } /// Returns the actual internal representation of the set. fn internal_repr(&self) -> InternalRepr { match self.explicit_internal_repr { Some(x) => x, None => match self.max_discrim { x if x < 8 && !self.internal_repr_force_array => InternalRepr::U8, x if x < 16 && !self.internal_repr_force_array => InternalRepr::U16, x if x < 32 && !self.internal_repr_force_array => InternalRepr::U32, x if x < 64 && !self.internal_repr_force_array => InternalRepr::U64, x => InternalRepr::Array((x as usize + 64) / 64), }, } } /// Returns the actual serde representation of the set. fn serde_repr(&self) -> SerdeRepr { match self.explicit_serde_repr { Some(x) => x, None => match self.max_discrim { x if x < 8 => SerdeRepr::U8, x if x < 16 => SerdeRepr::U16, x if x < 32 => SerdeRepr::U32, x if x < 64 => SerdeRepr::U64, x if x < 128 => SerdeRepr::U128, _ => SerdeRepr::Array, }, } } /// Validate the enumset type. fn validate(&self) -> Result<()> { // Gets the span of the maximum value. let largest_discriminant_span = match &self.max_discrim_span { Some(x) => *x, None => Span::call_site(), }; // Check if all bits of the bitset can fit in the memory representation, if one was given. if self.internal_repr().supported_variants() <= self.max_discrim as usize { error( largest_discriminant_span, "`repr` is too small to contain the largest discriminant.", )?; } // Check if all bits of the bitset can fit in the serialization representation. if let Some(supported_variants) = self.serde_repr().supported_variants() { if supported_variants <= self.max_discrim as usize { error( largest_discriminant_span, "`serialize_repr` is too small to contain the largest discriminant.", )?; } } Ok(()) } /// Returns a bitmask of all variants in the set. fn variant_map(&self) -> Vec { let mut vec = vec![0]; for variant in &self.variants { let (idx, bit) = (variant.variant_repr as usize / 64, variant.variant_repr % 64); while idx >= vec.len() { vec.push(0); } vec[idx] |= 1u64 << bit; } vec } } /// Generates the actual `EnumSetType` impl. fn enum_set_type_impl(info: EnumSetInfo, warnings: Vec<(Span, &'static str)>) -> SynTokenStream { let name = &info.name; let enumset = match &info.crate_name { Some(crate_name) => quote!(::#crate_name), None => { #[cfg(feature = "proc-macro-crate")] { use proc_macro_crate::FoundCrate; let crate_name = proc_macro_crate::crate_name("enumset"); match crate_name { Ok(FoundCrate::Name(name)) => { let ident = Ident::new(&name, Span::call_site()); quote!(::#ident) } _ => quote!(::enumset), } } #[cfg(not(feature = "proc-macro-crate"))] { quote!(::enumset) } } }; let typed_enumset = quote!(#enumset::EnumSet<#name>); let core = quote!(#enumset::__internal::core_export); let internal = quote!(#enumset::__internal); #[cfg(feature = "serde")] let serde = quote!(#enumset::__internal::serde); let repr = match info.internal_repr() { InternalRepr::U8 => quote! { u8 }, InternalRepr::U16 => quote! { u16 }, InternalRepr::U32 => quote! { u32 }, InternalRepr::U64 => quote! { u64 }, InternalRepr::U128 => quote! { u128 }, InternalRepr::Array(size) => quote! { #internal::ArrayRepr<{ #size }> }, }; let variant_map = info.variant_map(); let all_variants = match info.internal_repr() { InternalRepr::U8 | InternalRepr::U16 | InternalRepr::U32 | InternalRepr::U64 => { let lit = Literal::u64_unsuffixed(variant_map[0]); quote! { #lit } } InternalRepr::U128 => { let lit = Literal::u128_unsuffixed( variant_map[0] as u128 | variant_map.get(1).map_or(0, |x| (*x as u128) << 64), ); quote! { #lit } } InternalRepr::Array(size) => { let mut new = Vec::new(); for i in 0..size { new.push(Literal::u64_unsuffixed(*variant_map.get(i).unwrap_or(&0))); } quote! { #internal::ArrayRepr::<{ #size }>([#(#new,)*]) } } }; let ops = if info.no_ops { quote! {} } else { quote! { #[automatically_derived] impl> #core::ops::Sub for #name { type Output = #typed_enumset; fn sub(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) - other.into() } } #[automatically_derived] impl> #core::ops::BitAnd for #name { type Output = #typed_enumset; fn bitand(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) & other.into() } } #[automatically_derived] impl> #core::ops::BitOr for #name { type Output = #typed_enumset; fn bitor(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) | other.into() } } #[automatically_derived] impl> #core::ops::BitXor for #name { type Output = #typed_enumset; fn bitxor(self, other: O) -> Self::Output { #enumset::EnumSet::only(self) ^ other.into() } } #[automatically_derived] impl #core::ops::Not for #name { type Output = #typed_enumset; fn not(self) -> Self::Output { !#enumset::EnumSet::only(self) } } #[automatically_derived] impl #core::cmp::PartialEq<#typed_enumset> for #name { fn eq(&self, other: &#typed_enumset) -> bool { #enumset::EnumSet::only(*self) == *other } } } }; #[cfg(feature = "serde")] let serde_repr = info.serde_repr(); #[cfg(feature = "serde")] let serde_ops = match serde_repr { SerdeRepr::U8 | SerdeRepr::U16 | SerdeRepr::U32 | SerdeRepr::U64 | SerdeRepr::U128 => { let (serialize_repr, from_fn, to_fn) = match serde_repr { SerdeRepr::U8 => (quote! { u8 }, quote! { from_u8 }, quote! { to_u8 }), SerdeRepr::U16 => (quote! { u16 }, quote! { from_u16 }, quote! { to_u16 }), SerdeRepr::U32 => (quote! { u32 }, quote! { from_u32 }, quote! { to_u32 }), SerdeRepr::U64 => (quote! { u64 }, quote! { from_u64 }, quote! { to_u64 }), SerdeRepr::U128 => (quote! { u128 }, quote! { from_u128 }, quote! { to_u128 }), _ => unreachable!(), }; let check_unknown = if info.serialize_deny_unknown { quote! { if value & !#all_variants != 0 { use #serde::de::Error; return #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ) } } } else { quote! {} }; quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { let value = <#repr as #enumset::__internal::EnumSetTypeRepr>::#to_fn(&set.__priv_repr); #serde::Serialize::serialize(&value, ser) } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { let value = <#serialize_repr as #serde::Deserialize>::deserialize(de)?; #check_unknown let value = <#repr as #enumset::__internal::EnumSetTypeRepr>::#from_fn(value); #core::prelude::v1::Ok(#enumset::EnumSet { __priv_repr: value & #all_variants, }) } } } SerdeRepr::List => { let expecting_str = format!("a list of {}", name); quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { use #serde::ser::SerializeSeq; let mut seq = ser.serialize_seq(#core::prelude::v1::Some(set.len()))?; for bit in set { seq.serialize_element(&bit)?; } seq.end() } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, #expecting_str) } fn visit_seq( mut self, mut seq: A, ) -> #core::result::Result where A: #serde::de::SeqAccess<'de> { let mut accum = #enumset::EnumSet::<#name>::new(); while let #core::prelude::v1::Some(val) = seq.next_element::<#name>()? { accum |= val; } #core::prelude::v1::Ok(accum) } } de.deserialize_seq(Visitor) } } } SerdeRepr::Map => { let expecting_str = format!("a map from {} to bool", name); quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { use #serde::ser::SerializeMap; let mut map = ser.serialize_map(#core::prelude::v1::Some(set.len()))?; for bit in set { map.serialize_entry(&bit, &true)?; } map.end() } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, #expecting_str) } fn visit_map( mut self, mut map: A, ) -> #core::result::Result where A: #serde::de::MapAccess<'de> { let mut accum = #enumset::EnumSet::<#name>::new(); while let #core::prelude::v1::Some((val, is_present)) = map.next_entry::<#name, bool>()? { if is_present { accum |= val; } } #core::prelude::v1::Ok(accum) } } de.deserialize_map(Visitor) } } } SerdeRepr::Array => { let preferred_size = quote! { <<#name as #internal::EnumSetTypePrivate>::Repr as #internal::EnumSetTypeRepr> ::PREFERRED_ARRAY_LEN }; let (check_extra, convert_array) = if info.serialize_deny_unknown { ( quote! { if _val != 0 { return #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ) } }, quote! { match #enumset::EnumSet::<#name>::try_from_array(accum) { Some(x) => x, None => #core::prelude::v1::Err( D::Error::custom("enumset contains unknown bits") ), } }, ) } else { (quote! {}, quote! { #core::prelude::v1::Ok(#enumset::EnumSet::<#name>::from_array(accum)) }) }; quote! { fn serialize( set: #enumset::EnumSet<#name>, ser: S, ) -> #core::result::Result { // read the enum as an array let array = set.as_array::<{ #preferred_size }>(); // find the last non-zero value in the array let mut end = array.len(); for i in (0..array.len()).rev() { if array[i] != 0 { break; } end = i + 1; } // serialize the array #serde::Serialize::serialize(&array[..end], ser) } fn deserialize<'de, D: #serde::Deserializer<'de>>( de: D, ) -> #core::result::Result<#enumset::EnumSet<#name>, D::Error> { struct Visitor; impl <'de> #serde::de::Visitor<'de> for Visitor { type Value = #enumset::EnumSet<#name>; fn expecting( &self, formatter: &mut #core::fmt::Formatter, ) -> #core::fmt::Result { write!(formatter, "a list of u64") } fn visit_seq( mut self, mut seq: A, ) -> #core::result::Result where A: #serde::de::SeqAccess<'de> { let mut accum = [0; #preferred_size]; let mut i = 0; while let #core::prelude::v1::Some(val) = seq.next_element::()? { accum[i] = val; i += 1; if i == accum.len() { break; } } while let #core::prelude::v1::Some(_val) = seq.next_element::()? { #check_extra } #convert_array } } de.deserialize_seq(Visitor) } } } }; #[cfg(not(feature = "serde"))] let serde_ops = quote! {}; let is_uninhabited = info.variants.is_empty(); let is_zst = info.variants.len() == 1; let into_impl = if is_uninhabited { quote! { fn enum_into_u32(self) -> u32 { panic!(concat!(stringify!(#name), " is uninhabited.")) } unsafe fn enum_from_u32(val: u32) -> Self { panic!(concat!(stringify!(#name), " is uninhabited.")) } } } else if is_zst { let variant = &info.variants[0].name; quote! { fn enum_into_u32(self) -> u32 { self as u32 } unsafe fn enum_from_u32(val: u32) -> Self { #name::#variant } } } else { let variant_name: Vec<_> = info.variants.iter().map(|x| &x.name).collect(); let variant_value: Vec<_> = info.variants.iter().map(|x| x.variant_repr).collect(); let const_field: Vec<_> = ["IS_U8", "IS_U16", "IS_U32", "IS_U64", "IS_U128"] .iter() .map(|x| Ident::new(x, Span::call_site())) .collect(); let int_type: Vec<_> = ["u8", "u16", "u32", "u64", "u128"] .iter() .map(|x| Ident::new(x, Span::call_site())) .collect(); quote! { fn enum_into_u32(self) -> u32 { self as u32 } unsafe fn enum_from_u32(val: u32) -> Self { // We put these in const fields so the branches they guard aren't generated even // on -O0 #(const #const_field: bool = #core::mem::size_of::<#name>() == #core::mem::size_of::<#int_type>();)* match val { // Every valid variant value has an explicit branch. If they get optimized out, // great. If the representation has changed somehow, and they don't, oh well, // there's still no UB. #(#variant_value => #name::#variant_name,)* // Helps hint to the LLVM that this is a transmute. Note that this branch is // still unreachable. #(x if #const_field => { let x = x as #int_type; *(&x as *const _ as *const #name) })* // Default case. Sometimes causes LLVM to generate a table instead of a simple // transmute, but, oh well. _ => #core::hint::unreachable_unchecked(), } } } }; let eq_impl = if is_uninhabited { quote!(panic!(concat!(stringify!(#name), " is uninhabited."))) } else { quote!((*self as u32) == (*other as u32)) }; let super_impls = if info.no_super_impls { quote! {} } else { quote! { #[automatically_derived] impl #core::cmp::PartialEq for #name { fn eq(&self, other: &Self) -> bool { #eq_impl } } #[automatically_derived] impl #core::cmp::Eq for #name { } #[automatically_derived] #[allow(clippy::expl_impl_clone_on_copy)] impl #core::clone::Clone for #name { fn clone(&self) -> Self { *self } } #[automatically_derived] impl #core::marker::Copy for #name { } } }; let impl_with_repr = if info.explicit_internal_repr.is_some() { quote! { #[automatically_derived] unsafe impl #enumset::EnumSetTypeWithRepr for #name { type Repr = #repr; } } } else { quote! {} }; let inherent_impl_blocks = match info.internal_repr() { InternalRepr::U8 | InternalRepr::U16 | InternalRepr::U32 | InternalRepr::U64 | InternalRepr::U128 => { let self_as_repr_mask = if is_uninhabited { quote! { 0 } // impossible anyway } else { quote! { 1 << self as #repr } }; quote! { #[automatically_derived] #[doc(hidden)] impl #name { /// Creates a new enumset with only this variant. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_only( self, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: #self_as_repr_mask } } } #[automatically_derived] #[doc(hidden)] impl __EnumSetConstHelper { pub const fn const_union( &self, chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: chain_a.__priv_repr | chain_b.__priv_repr, } } pub const fn const_intersection( &self, chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: chain_a.__priv_repr & chain_b.__priv_repr, } } pub const fn const_symmetric_difference( &self, chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { #enumset::EnumSet { __priv_repr: chain_a.__priv_repr ^ chain_b.__priv_repr, } } pub const fn const_complement( &self, chain: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let mut all = #enumset::EnumSet::<#name>::all(); #enumset::EnumSet { __priv_repr: !chain.__priv_repr & all.__priv_repr, } } } } } InternalRepr::Array(size) => { quote! { #[automatically_derived] #[doc(hidden)] impl #name { /// Creates a new enumset with only this variant. #[deprecated(note = "This method is an internal implementation detail \ generated by the `enumset` crate's procedural macro. It \ should not be used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_only( self, ) -> #enumset::EnumSet<#name> { let mut set = #enumset::EnumSet::<#name> { __priv_repr: #internal::ArrayRepr::<{ #size }>([0; #size]), }; let bit = self as u32; let (idx, bit) = (bit as usize / 64, bit % 64); set.__priv_repr.0[idx] |= 1u64 << bit; set } } #[automatically_derived] #[doc(hidden)] impl __EnumSetConstHelper { pub const fn const_union( &self, mut chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let mut i = 0; while i < #size { chain_a.__priv_repr.0[i] |= chain_b.__priv_repr.0[i]; i += 1; } chain_a } pub const fn const_intersection( &self, mut chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let mut i = 0; while i < #size { chain_a.__priv_repr.0[i] &= chain_b.__priv_repr.0[i]; i += 1; } chain_a } pub const fn const_symmetric_difference( &self, mut chain_a: #enumset::EnumSet<#name>, chain_b: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let mut i = 0; while i < #size { chain_a.__priv_repr.0[i] ^= chain_b.__priv_repr.0[i]; i += 1; } chain_a } pub const fn const_complement( &self, mut chain: #enumset::EnumSet<#name>, ) -> #enumset::EnumSet<#name> { let mut all = #enumset::EnumSet::<#name>::all(); let mut i = 0; while i < #size { let new = !chain.__priv_repr.0[i] & all.__priv_repr.0[i]; chain.__priv_repr.0[i] = new; i += 1; } chain } } } } }; let mut generated_warnings = SynTokenStream::new(); for (span, warning) in warnings { generated_warnings.extend(quote_spanned! { span => { #[deprecated(note = #warning)] #[allow(non_upper_case_globals)] const _w: () = (); let _ = _w; } }); } let bit_width = info.max_discrim + 1; let variant_count = info.variants.len() as u32; let vis = &info.vis; quote! { const _: () = { #[automatically_derived] #[doc(hidden)] #vis struct __EnumSetConstHelper; #[automatically_derived] #[doc(hidden)] impl #name { /// Creates a new enumset helper. #[deprecated(note = "This method is an internal implementation detail generated \ by the `enumset` crate's procedural macro. It should not be \ used directly.")] #[doc(hidden)] pub const fn __impl_enumset_internal__const_helper( self, ) -> __EnumSetConstHelper { __EnumSetConstHelper } } #[automatically_derived] unsafe impl #internal::EnumSetTypePrivate for #name { type ConstHelper = __EnumSetConstHelper; const CONST_HELPER_INSTANCE: __EnumSetConstHelper = __EnumSetConstHelper; type Repr = #repr; const ALL_BITS: Self::Repr = #all_variants; const BIT_WIDTH: u32 = #bit_width; const VARIANT_COUNT: u32 = #variant_count; #into_impl #serde_ops } #[automatically_derived] unsafe impl #enumset::EnumSetType for #name { } #impl_with_repr #super_impls #ops #inherent_impl_blocks fn __enumset_derive__generated_warnings() { #generated_warnings } }; } } #[proc_macro_derive(EnumSetType, attributes(enumset))] pub fn derive_enum_set_type(input: TokenStream) -> TokenStream { let input: DeriveInput = parse_macro_input!(input); let input_span = input.span(); let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) { Ok(attrs) => attrs, Err(e) => return e.write_errors().into(), }; derive_enum_set_type_0(input, attrs, input_span).unwrap_or_else(|e| e.to_compile_error().into()) } fn derive_enum_set_type_0( input: DeriveInput, attrs: EnumsetAttrs, _input_span: Span, ) -> Result { if !input.generics.params.is_empty() { error( input.generics.span(), "`#[derive(EnumSetType)]` cannot be used on enums with type parameters.", ) } else if let Data::Enum(data) = &input.data { let mut info = EnumSetInfo::new(&input, &attrs); let mut warnings = Vec::new(); // Check enum repr for attr in &input.attrs { if attr.path().is_ident("repr") { let meta: Ident = attr.parse_args()?; match meta.to_string().as_str() { "C" | "Rust" => {} "u8" | "u16" | "u32" | "u64" | "u128" | "usize" => {} "i8" | "i16" | "i32" | "i64" | "i128" | "isize" => {} x => error( attr.span(), format!("`#[repr({})]` cannot be used on enumset variants.", x), )?, } } } // Parse internal representations if let Some(repr) = &*attrs.repr { info.push_repr(attrs.repr.span(), repr)?; } // Parse serialization representations if let Some(serialize_repr) = &*attrs.serialize_repr { info.push_serialize_repr(attrs.serialize_repr.span(), serialize_repr)?; } if *attrs.serialize_as_map { info.explicit_serde_repr = Some(SerdeRepr::Map); warnings.push(( attrs.serialize_as_map.span(), "#[enumset(serialize_as_map)] is deprecated. \ Use `#[enumset(serialize_repr = \"map\")]` instead.", )); } if *attrs.serialize_as_list { // in old versions, serialize_as_list will override serialize_as_map info.explicit_serde_repr = Some(SerdeRepr::List); warnings.push(( attrs.serialize_as_list.span(), "#[enumset(serialize_as_list)] is deprecated. \ Use `#[enumset(serialize_repr = \"list\")]` instead.", )); } #[cfg(feature = "std_deprecation_warning")] { warnings.push(( _input_span, "feature = \"std\" is depercated. If you rename `enumset`, use \ feature = \"proc-macro-crate\" instead. If you don't, remove the feature.", )); } // Parse enum variants for variant in &data.variants { info.push_variant(variant)?; } // Validate the enumset info.validate()?; // Generates the actual `EnumSetType` implementation Ok(enum_set_type_impl(info, warnings).into()) } else { error(input.span(), "`#[derive(EnumSetType)]` may only be used on enums") } }