[][src]Macro phantom_fields::phantom_fields

phantom_fields!() { /* proc-macro */ }

Declares a struct to have "phantom" fields. Use within an impl block.

At the moment, this only supports tuple structs and only targets the 0 field of said structs. It's intended for use with newtype'd unsigned integers of various sizes.

The proc-macro's grammar is fairly simple once you get the hang of it but there's several possibilities you can declare.

  • self.0: [TYPE], to declare the type of the self.0 field.
  • Then you declare 0 or more phantom fields:
    • Each phantom field can have attributes, which is mostly intended for allowing rustdoc.
    • Then the name of the field, followed by :
    • Then you describe "where" the field is with a , at the end:
      • A single integer makes a bool field at that bit.
      • An a-b integer span makes an integer field in that inclusive bit range.
      • An a-b=ENUM<TAG1,...,TAG_N> location makes an enum field of the type given that can be any of the tags you specify. In the decoding method an _ => unreachable!() branch is placed after all tags, so if you specify less tags than possible bit patterns (eg: only 3 tags for a 2-bit field) your code can potentially panic.
      • An identifier will assume that the identifier is already a const declared somewhere in scope, and use that as the bit location of a bool-typed field.
  • Enum and integer phantom fields also get a FOO_MASK const added to the struct type for each field named foo. This const is what takes on the attributes you specified for that phantom field.
  • Bool phantom fields get a FOO_BIT const added to the struct for each field named foo. This gets the attributes of the field.
  • Const phantom fields don't get added to the struct, they're already a const somewhere after all.

Once this is all set up you'll of course want to use the phantom fields:

  • phantom fields can be read using their name as a the method name, similar to normal rust "getters".
  • phantom fields can be replaced "builder style" by taking self and giving a new value using with_field. This is const, so you can use it to declare a const that has some particular setting combination.

I think it makes more sense if you see it in action. Here's an extended example of all the situations supported.

use phantom_fields::phantom_fields;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum DisplayMode {
  Good = 0,
  Bad = 1,
  Ugly = 2,
}

pub const CONST_TEST_VALUE: u32 = 1 << 13;

#[derive(Debug, Default, Clone, Copy)]
#[repr(transparent)]
pub struct PhantomFieldsDemo(u32);

impl PhantomFieldsDemo {
  phantom_fields! {
    self.0: u32,
    /// enum_example docs
    enum_example: 0-2=DisplayMode<Good, Bad, Ugly>,
    bool_example: 3,
    /// This gives us a 2-bit field
    int_example: 6-7,
    const_example: CONST_TEST_VALUE,
  }
}