]> git.lizzy.rs Git - enumset.git/commitdiff
Documentation improvements.
authorLymia Aluysia <lymia@lymiahugs.com>
Sat, 4 Apr 2020 22:19:05 +0000 (15:19 -0700)
committerLymia Aluysia <lymia@lymiahugs.com>
Sat, 4 Apr 2020 22:19:05 +0000 (15:19 -0700)
enumset/src/lib.rs
enumset_derive/src/lib.rs

index 36431e034fb2f9c5635a5fb8425a793adcd9f58b..9c5bb464bcbe6a43da3475aafaa16224d2bc39c2 100644 (file)
@@ -18,6 +18,8 @@
 //! }
 //! ```
 //!
+//! For more information on more advanced use cases, see the documentation for [`EnumSetType`].
+//!
 //! # Working with EnumSets
 //!
 //! EnumSets can be constructed via [`EnumSet::new()`] like a normal set. In addition,
@@ -60,7 +62,7 @@
 //! assert_eq!(CONST_SET, Enum::A | Enum::B);
 //! ```
 //!
-//! Mutable operations on the [`EnumSet`] otherwise work basically as expected:
+//! Mutable operations on the [`EnumSet`] otherwise similarly to Rust's builtin sets:
 //!
 //! ```rust
 //! # use enumset::*;
@@ -103,14 +105,24 @@ pub mod __internal {
 
     /// The actual members of EnumSetType. Put here to avoid polluting global namespaces.
     pub unsafe trait EnumSetTypePrivate {
+        /// The underlying type used to store the bitset.
         type Repr: EnumSetTypeRepr;
+        /// A mask of bits that are valid in the bitset.
         const ALL_BITS: Self::Repr;
+
+        /// Converts an enum of this type into its bit position.
         fn enum_into_u32(self) -> u32;
+        /// Converts a bit position into an enum value.
         unsafe fn enum_from_u32(val: u32) -> Self;
 
+        /// Serializes the `EnumSet`.
+        ///
+        /// This and `deserialize` are part of the `EnumSetType` trait so the procedural derive
+        /// can control how `EnumSet` is serialized.
         #[cfg(feature = "serde")]
         fn serialize<S: serde::Serializer>(set: EnumSet<Self>, ser: S) -> Result<S::Ok, S::Error>
             where Self: EnumSetType;
+        /// Deserializes the `EnumSet`.
         #[cfg(feature = "serde")]
         fn deserialize<'de, D: serde::Deserializer<'de>>(de: D) -> Result<EnumSet<Self>, D::Error>
             where Self: EnumSetType;
@@ -123,6 +135,8 @@ use crate::__internal::EnumSetTypePrivate;
 mod private {
     use super::*;
 
+    /// A trait marking valid underlying bitset storage types and providing the
+    /// operations `EnumSet` and related types use.
     pub trait EnumSetTypeRepr :
         PrimInt + WrappingSub + CheckedShl + Debug + Hash + FromPrimitive + ToPrimitive +
         AsPrimitive<u8> + AsPrimitive<u16> + AsPrimitive<u32> + AsPrimitive<u64> +
@@ -161,10 +175,13 @@ use crate::private::EnumSetTypeRepr;
 /// The trait used to define enum types that may be used with [`EnumSet`].
 ///
 /// This trait should be implemented using `#[derive(EnumSetType)]`. Its internal structure is
-/// not currently stable, and may change at any time.
+/// not stable, and may change at any time.
 ///
 /// # Custom Derive
 ///
+/// Any C-like enum is supported, as long as there are no more than 128 variants in the enum,
+/// and no variant discriminator is larger than 127.
+///
 /// The custom derive for [`EnumSetType`] automatically creates implementations of [`PartialEq`],
 /// [`Sub`], [`BitAnd`], [`BitOr`], [`BitXor`], and [`Not`] allowing the enum to be used as
 /// if it were an [`EnumSet`] in expressions. This can be disabled by adding an `#[enumset(no_ops)]`
@@ -173,8 +190,12 @@ use crate::private::EnumSetTypeRepr;
 /// The custom derive for `EnumSetType` automatically implements [`Copy`], [`Clone`], [`Eq`], and
 /// [`PartialEq`] on the enum. These are required for the [`EnumSet`] to function.
 ///
-/// Any C-like enum is supported, as long as there are no more than 128 variants in the enum,
-/// and no variant discriminator is larger than 127.
+/// In addition, if you have renamed the `enumset` crate in your crate, you can use the
+/// `#[enumset(crate_name = "enumset2")]` attribute to tell the custom derive to use that name
+/// instead.
+///
+/// Attributes controlling the serialization of an `EnumSet` are documented in
+/// [its documentation](./struct.EnumSet.html#serialization).
 ///
 /// # Examples
 ///
@@ -213,23 +234,29 @@ pub unsafe trait EnumSetType: Copy + Eq + EnumSetTypePrivate { }
 /// An efficient set type for enums.
 ///
 /// It is implemented using a bitset stored using the smallest integer that can fit all bits
-/// in the underlying enum.
+/// in the underlying enum. In general, an enum variant with a numeric value of `n` is stored in
+/// the nth least significant bit (corresponding with a mask of, e.g. `1 << enum as u32`).
 ///
 /// # Serialization
 ///
-/// By default, `EnumSet`s are serialized as an unsigned integer of the same width as used to store
-/// it in memory.
+/// When the `serde` feature is enabled, `EnumSet`s can be serialized and deserialized using
+/// the `serde` crate. The exact serialization format can be controlled with additional attributes
+/// on the enum type. These attributes are valid regardless of whether the `serde` feature
+/// is enabled.
 ///
-/// Unknown bits are ignored, and are simply dropped. To override this behavior, you can add a
-/// `#[enumset(serialize_deny_unknown)]` annotation to your enum.
+/// By default, `EnumSet`s serialize by directly writing out the underlying bitset as an integer
+/// of the smallest type that can fit in the underlying enum. You can add a
+/// `#[enumset(serialize_repr = "u8")]` attribute to your enum to control the integer type used
+/// for serialization. This can be important for avoiding unintentional breaking changes when
+/// `EnumSet`s are serialized with formats like `bincode`.
 ///
-/// You can add a `#[enumset(serialize_repr = "u8")]` annotation to your enum to manually set
-/// the number width the `EnumSet` is serialized as. Only unsigned integer types may be used. This
-/// can be used to avoid breaking changes in certain serialization formats (such as `bincode`).
+/// By default, unknown bits are ignored and silently removed from the bitset. To override this
+/// behavior, you can add a `#[enumset(serialize_deny_unknown)]` attribute. This will cause
+/// deserialization to fail if an invalid bit is set.
 ///
-/// In addition, the `#[enumset(serialize_as_list)]` annotation causes the `EnumSet` to be
+/// In addition, the `#[enumset(serialize_as_list)]` attribute causes the `EnumSet` to be
 /// instead serialized as a list of enum variants. This requires your enum type implement
-/// [`Serialize`] and [`Deserialize`].
+/// [`Serialize`] and [`Deserialize`]. Note that this is a breaking change
 #[derive(Copy, Clone, PartialEq, Eq)]
 pub struct EnumSet<T: EnumSetType> {
     #[doc(hidden)]
@@ -386,6 +413,7 @@ impl <T: EnumSetType> EnumSet<T> {
     }
 }
 
+/// Helper macro for generating conversion functions.
 macro_rules! conversion_impls {
     (
         $(for_num!(
@@ -397,7 +425,7 @@ macro_rules! conversion_impls {
         impl <T : EnumSetType> EnumSet<T> {$(
             #[doc = "Returns a `"]
             #[doc = $underlying_str]
-            #[doc = "` representing the elements of this set. \n\nIf the underlying bitset will \
+            #[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
                      not fit in a `"]
             #[doc = $underlying_str]
             #[doc = "`, this method will panic."]
@@ -407,7 +435,7 @@ macro_rules! conversion_impls {
 
             #[doc = "Tries to return a `"]
             #[doc = $underlying_str]
-            #[doc = "` representing the elements of this set. \n\nIf the underlying bitset will \
+            #[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
                      not fit in a `"]
             #[doc = $underlying_str]
             #[doc = "`, this method will instead return `None`."]
@@ -417,7 +445,7 @@ macro_rules! conversion_impls {
 
             #[doc = "Returns a truncated `"]
             #[doc = $underlying_str]
-            #[doc = "` representing the elements of this set. \n\nIf the underlying bitset will \
+            #[doc = "` representing the elements of this set.\n\nIf the underlying bitset will \
                      not fit in a `"]
             #[doc = $underlying_str]
             #[doc = "`, this method will truncate any bits that don't fit."]
@@ -427,7 +455,7 @@ macro_rules! conversion_impls {
 
             #[doc = "Constructs a bitset from a `"]
             #[doc = $underlying_str]
-            #[doc = "`. \n\nIf a bit that doesn't correspond to an enum variant is set, this \
+            #[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
                      method will panic."]
             pub fn $from(bits: $underlying) -> Self {
                 Self::$try_from(bits).expect("Bitset contains invalid variants.")
@@ -435,7 +463,7 @@ macro_rules! conversion_impls {
 
             #[doc = "Attempts to constructs a bitset from a `"]
             #[doc = $underlying_str]
-            #[doc = "`. \n\nIf a bit that doesn't correspond to an enum variant is set, this \
+            #[doc = "`.\n\nIf a bit that doesn't correspond to an enum variant is set, this \
                      method will return `None`."]
             pub fn $try_from(bits: $underlying) -> Option<Self> {
                 let bits = <T::Repr as FromPrimitive>::$from_fn(bits);
@@ -458,7 +486,6 @@ macro_rules! conversion_impls {
         )*}
     }
 }
-
 conversion_impls! {
     for_num!(u8, "u8", from_u8, to_u8,
              from_u8 try_from_u8 from_u8_truncated as_u8 try_as_u8 as_u8_truncated);
index ab983430fd35789f2030357c66b17c4d8957f9eb..83ac1381cf89f0d44331bc42689bb53ba5a6a82c 100644 (file)
@@ -31,27 +31,42 @@ struct EnumsetAttrs {
 
 /// 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<Ident>,
+    /// The numeric type to serialize the enum as.
     explicit_serde_repr: Option<Ident>,
+    /// Whether the underlying repr of the enum supports negative values.
     has_signed_repr: bool,
+    /// Whether the underlying repr of the enum supports values higher than 2^32.
     has_large_repr: bool,
+    /// A list of variants in the enum.
     variants: Vec<EnumSetValue>,
 
+    /// The highest encountered variant discriminant.
     max_discrim: u32,
+    /// 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<String>,
-    used_discriminators: HashSet<u32>,
+    /// A list of variant discriminants that are already in use.
+    used_discriminants: HashSet<u32>,
 
+    /// Avoid generating operator overloads on the enum type.
     no_ops: bool,
+    /// Serialize the enum as a list.
     serialize_as_list: bool,
+    /// Disallow unknown bits while deserializing the enum.
     serialize_deny_unknown: bool,
 }
 impl EnumSetInfo {
@@ -66,14 +81,17 @@ impl EnumSetInfo {
             max_discrim: 0,
             cur_discrim: 0,
             used_variant_names: HashSet::new(),
-            used_discriminators: HashSet::new(),
+            used_discriminants: HashSet::new(),
             no_ops: attrs.no_ops,
             serialize_as_list: attrs.serialize_as_list,
             serialize_deny_unknown: attrs.serialize_deny_unknown
         }
     }
 
+    /// Sets an explicit repr for the enumset.
     fn push_explicit_repr(&mut self, attr_span: Span, repr: &str) -> Result<()> {
+        // Check whether the repr is supported, and if so, set some flags for better error
+        // messages later on.
         match repr {
             "Rust" | "C" | "u8" | "u16" | "u32" => Ok(()),
             "usize" | "u64" | "u128" => {
@@ -92,10 +110,12 @@ impl EnumSetInfo {
             _ => error(attr_span, "Unsupported repr.")
         }
     }
+    /// 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 {
                 let discriminant_fail_message = format!(
                     "Enum set discriminants must be `u32`s.{}",
@@ -124,6 +144,7 @@ impl EnumSetInfo {
                 }
             }
 
+            // Validate the discriminant.
             let discriminant = self.cur_discrim;
             if discriminant >= 128 {
                 let message = if self.variants.len() <= 127 {
@@ -133,11 +154,11 @@ impl EnumSetInfo {
                 };
                 error(variant.span(), message)?;
             }
-
-            if self.used_discriminators.contains(&discriminant) {
+            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;
@@ -147,14 +168,16 @@ impl EnumSetInfo {
                 variant_repr: discriminant,
             });
             self.used_variant_names.insert(variant.ident.to_string());
-            self.used_discriminators.insert(discriminant);
+            self.used_discriminants.insert(discriminant);
 
             Ok(())
         } else {
             error(variant.span(), "`#[derive(EnumSetType)]` can only be used on fieldless enums.")
         }
     }
+    /// Validate the enumset type.
     fn validate(&self) -> Result<()> {
+        // Check if all bits of the bitset can fit in the serialization representation.
         if let Some(explicit_serde_repr) = &self.explicit_serde_repr {
             let is_overflowed = match explicit_serde_repr.to_string().as_str() {
                 "u8" => self.max_discrim >= 8,
@@ -174,6 +197,7 @@ impl EnumSetInfo {
         Ok(())
     }
 
+    /// Computes the underlying type used to store the enumset.
     fn enumset_repr(&self) -> SynTokenStream {
         if self.max_discrim <= 7 {
             quote! { u8 }
@@ -189,6 +213,7 @@ impl EnumSetInfo {
             panic!("max_variant > 127?")
         }
     }
+    /// Computes the underlying type used to serialize the enumset.
     #[cfg(feature = "serde")]
     fn serde_repr(&self) -> SynTokenStream {
         if let Some(serde_repr) = &self.explicit_serde_repr {
@@ -198,6 +223,7 @@ impl EnumSetInfo {
         }
     }
 
+    /// Returns a bitmask of all variants in the set.
     fn all_variants(&self) -> u128 {
         let mut accum = 0u128;
         for variant in &self.variants {
@@ -208,6 +234,7 @@ impl EnumSetInfo {
     }
 }
 
+/// Generates the actual `EnumSetType` impl.
 fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream {
     let name = &info.name;
     let enumset = match &info.crate_name {
@@ -376,7 +403,8 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream {
                 self as u32
             }
             unsafe fn enum_from_u32(val: u32) -> Self {
-                // We put these in const fields so they aren't generated even on -O0
+                // 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 {
@@ -431,7 +459,20 @@ fn enum_set_type_impl(info: EnumSetInfo) -> SynTokenStream {
     }
 }
 
-fn derive_enum_set_type_impl(input: DeriveInput, attrs: EnumsetAttrs) -> Result<TokenStream> {
+/// A wrapper that parses the input enum.
+#[proc_macro_derive(EnumSetType, attributes(enumset))]
+pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
+    let input: DeriveInput = parse_macro_input!(input);
+    let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) {
+        Ok(attrs) => attrs,
+        Err(e) => return e.write_errors().into(),
+    };
+    match derive_enum_set_type_0(input, attrs) {
+        Ok(v) => v,
+        Err(e) => e.to_compile_error().into(),
+    }
+}
+fn derive_enum_set_type_0(input: DeriveInput, attrs: EnumsetAttrs) -> Result<TokenStream> {
     if !input.generics.params.is_empty() {
         error(
             input.generics.span(),
@@ -453,17 +494,4 @@ fn derive_enum_set_type_impl(input: DeriveInput, attrs: EnumsetAttrs) -> Result<
     } else {
         error(input.span(), "`#[derive(EnumSetType)]` may only be used on enums")
     }
-}
-
-#[proc_macro_derive(EnumSetType, attributes(enumset))]
-pub fn derive_enum_set_type(input: TokenStream) -> TokenStream {
-    let input: DeriveInput = parse_macro_input!(input);
-    let attrs: EnumsetAttrs = match EnumsetAttrs::from_derive_input(&input) {
-        Ok(attrs) => attrs,
-        Err(e) => return e.write_errors().into(),
-    };
-    match derive_enum_set_type_impl(input, attrs) {
-        Ok(v) => v,
-        Err(e) => e.to_compile_error().into(),
-    }
 }
\ No newline at end of file