[−][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 theself.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 namedfoo
. 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 usingwith_field
. This isconst
, 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, } }