Skip to main content

ecore_macro/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::num::NonZero;
4
5enum Either<Left, Right> {
6    Left(Left),
7    Right(Right),
8}
9impl<Left: syn::parse::Parse, Right: syn::parse::Parse> syn::parse::Parse for Either<Left, Right> {
10    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
11        if let Ok(left) = input.parse::<Left>() { Ok(Self::Left(left)) } else { Ok(Self::Right(input.parse::<Right>()?)) }
12    }
13}
14
15fn error<T>(span: proc_macro2::Span, message: impl std::fmt::Display) -> syn::Result<T> {
16    Err(syn::Error::new(span, message))
17}
18
19/// ## Derive MapEnum for enum
20///
21/// **note: keep variant value same as define order (0..variants_count) will get optimized runtime performance**,
22///
23/// For tagged enum must add 'discriminant' or 'discriminant(attr_for_discriminant_enum)' attribute to define additional discriminant enum
24/// ```ignore
25/// #[derive(MapEnum)]
26/// #[discriminant(derive(Clone, Copy))]
27/// enum Enum1 {
28///     Arm1(u8),
29///     Arm2
30/// }
31/// ```
32/// will generate discriminant enum additionally:
33/// ```ignore
34/// #[derive(Clone, Copy)]
35/// enum Enum1Discriminant {
36///     Arm1,
37///     Arm2
38/// }
39/// ```
40#[proc_macro_derive(MapEnum, attributes(discriminant))]
41pub fn map_enum_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
42    unwrap_stream(map_enum::map_enum_derive(item))
43}
44
45/// Map enum to `EnumMap`, macro version for `EnumMap::map_new`, can move value, can invoke in const, requires import `EnumMap` and `MapEnum`
46///
47/// ```ignore
48/// #[derive(MapEnum)]
49/// enum Enum1 {
50///     Var0,
51///     Var1,
52/// }
53///
54/// const ENUM1: EnumMap<Enum1, u8> = map_enum!(match Enum1 {
55///     Var0 => 2,
56///     Var1 => 5,
57/// });
58///
59/// #[derive(MapEnum)]
60/// enum Enum2 {
61///     Kar0,
62///     Kar1,
63/// }
64///
65/// /// support map multi level enum using tuple,
66/// const ENUM2: EnumMap<Enum1, EnumMap<Enum2, u8>> = map_enum!(match (Enum1, Enum2) {
67///     (Var0, Kar0) => 2,
68///     (Var0, Kar1) => 5,
69///     (Var1, Kar0) => 8,
70///     (Var1, Kar1) => 9,
71/// });
72/// ```
73#[proc_macro]
74pub fn map_enum(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
75    unwrap_stream(map_enum::map_enum(item))
76}
77
78/// Derive `IterEnumDiscriminants` for enum, providing compile-time iteration over
79/// all discriminant names and their associated integer values.
80///
81/// This generates a constant iterator that can be used with `ecore::EnumDiscriminantsIterator`
82/// to iterate over all variants and their associated metadata at runtime.
83#[proc_macro_derive(IterEnumDiscriminants)]
84pub fn iter_enum_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
85    unwrap_stream(iter_enum::iter_enum_derive(item))
86}
87
88/// ## Derive BitsCast (for enum only right now)
89///
90/// Inside usage but not recommanded:
91/// ```ignore
92/// #[derive(BitsCast, Clone, Copy)] // must derive `Clone` and `Copy` too
93/// #[bitfld(u8)] // must has helper attribute
94/// enum {...}
95/// ```
96/// Recommand usage:
97/// ```ignore
98/// #[bitfld(u8)] // attribute macro `bitfld` will auto expand to correct usage
99/// enum {...}
100/// ```
101#[proc_macro_derive(BitsCast, attributes(bitfld))]
102pub fn derive_bits_cast(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
103    unwrap_stream(bitfld::bitenum::derive_bits_cast(item))
104}
105
106/// ## Define bit field struct or bit field enum or bit field
107///
108/// ### Define bit enum
109/// mark a enum can be used inside bit struct, if enum defined every value valid for repr type then impl `BitsCast`, otherwise impl `Uncheckable`
110/// , derive [Clone] and [Copy] implicitly, default `#[repr(primary int of bits type)]` if not add `#[repr(..)]` attribute
111/// ```ignore
112/// #[bitfld(u3)] // u3 is bits type, actually expand to `#[derive(BitsCast, Clone, Copy)] #[bitfld(u3)] #[repr(u8)]`
113/// #[derive(MapEnum)] // if requires `EnumMap<BitEnum1, ..>`
114/// enum BitEnum1 {
115///     A = 0,
116///     B = 1
117/// }
118/// ```
119/// For fieldness enum, must set `tag(ty, range)` and maybe set `payload(ty, range)` to define bits layout, `range` can be singlebit (like 2) or
120///  bit range (like 1..3 or 1..=2) or start:bits (like 1:2). `range` is optional, if omited in `tag` then default is start from zero,
121///  if omited in `payload` then default is start from tag end until total end. `tag` type `ty` must be int type but `payload` type `ty` must be unsigned int type
122/// ```ignore
123/// #[bitfld(u4, tag(u1, 0), payload(u3, 1..4))]
124/// #[derive(MapEnum)] // if requires `EnumMap<BitEnum1, ..>`
125/// enum BitEnum2 {
126///     A(u2) = 0,
127///     B(u3) = 1
128/// }
129/// ```
130/// ### Define bit field struct, note:
131/// * ****Every field in struct will be convert to a method to get bit field ref****
132/// * because fields will be convert to method, `#[bitfld(...)]` maybe needs to add before other attributes if it requires real field
133/// * underlying type must be unsigned basic int (u1, u2, ..., u128)
134/// * bit field type can be any `T: BitsCast` type and `[T; N]` array and `EnumMap<Enum, T> where Enum: MapEnum`,
135///   includes u1, u2, ..., u128, i1, i2, ..., i128, bool, any bit struct and any bit enum
136/// * derive [Clone] and [Copy] and `bytemuck::Zeroable` and [`BitsCast`], derive `bytemuck::Pod` and `ConvertEndian` or `Uncheckable` only if avaiable
137/// * default `#[repr(transparent)]` if not add `#[repr(..)]` attribute
138/// ```ignore
139/// #[bitfld(pub u32, relative)] // using #[bitfld(pub u32, .., overlay, ..)] will allow overlay for all fields, u32 is underlying type
140/// struct BitStruct1 {
141///     pub a: bitfld!(bool, 1), // single bit, same as 1..2 or 1..=1 or 1:1
142///
143///     pub b: bitfld!(BitEnum1, 2..=4), // same as 2..5 or 2:3
144///
145///     pub c: bitfld!(u5, 2..7, overlay), // same as 2..=6 or 2:5, reuqires `overlay` here because overlay field `b`
146///
147///     pub d: bitfld!(i3, :3), // occupy 3 bits, start bit use next bit of pre defined bit field, same as 7..10 or 7..=9 or 7:3 here
148///
149///     pub e: bitfld!(bool, ..11), // start bit use next bit of pre defined bit field, same as 10..11 or 10..=10 or 10:1 here
150///
151///     pub f: bitfld!([u2; 2], 11:4)  // same as 11..15 or 11..=14 here, array of any `BitsCast` type is allowed
152///
153///     pub g: bitfld!(EnumMap<BitEnum1, u4>, 15:4) // same as 15..19 or 15..=18 here, enum map of any `BitsCast` type is allowed
154///
155///     #[bitfld(19..=22)] // same as ..=22 or 19..23 or ..23 or 19:4 or :4, syntax same as bitfld!(..) without the first bit type
156///     pub h: EnumMap<BitEnum1, u4>, // using attribute to define bit field is also ok
157///
158///     pub i: bitfld!(u9, ..), // from next bit of pre defined bit field, until end of underlying, same as 23..32 or 23..=31 or 23:9 here
159/// }
160/// ```
161/// ### Define bit field
162/// using attribute `#[bitfld(range, attributes..)]` or macro type `bitfld!(bittype, range, attributes..)` to define bit field
163/// * absolute range `a..b` or `a..=b` or single bit `a`(= `a..a+1` or `a..=a`) or `a:bits`(= `a..a+bits`)
164/// * relative range, start from next bit of pre defined bit field, `..b` or `..=b` or `:bits`, requires `relative` in bit struct's #[bitfld(..)]
165/// * relative tail range, `a..` or `..`, occupy until underlying end, requires `relative` in bit struct's #[bitfld(..)]
166#[proc_macro_attribute]
167pub fn bitfld(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
168    unwrap_stream(bitfld::bitfld(attr, item))
169}
170
171/// Generate `RInt` type from value range, auto select `RRxx` value type
172/// * full form `rint!(MIN..=MAX, default = DEFAULT, step = STEP, bits = BITS)`, `MIN` and `MAX` is required, others is optional
173/// * range `MIN..=MAX` is same as `MIN..MAX+1``
174/// * if `default` not setted, then is `MIN`
175/// * if `step` not setted, then is 1, `step` must >= 1, and must ensure `MIN % STEP == 0 && MAX % STEP == 0 && DEFAULT % STEP == 0`
176/// * if `bits` not setted, then is `((MAX - MIN) / STEP).bit_width()`
177/// ```ignore
178/// rint!(-10..=100) // RInt<u7, RRI8<-10, 100>>
179/// rint!(-10..=100, default = 3) // RInt<u7, RRI8<-10, 100, 3>>
180/// rint!(-10..=100, default = 20, step = 10) // RInt<u4, RRI8<-10, 100, 20, 10>>
181/// rint!(3..50, default = 20) // RInt<u6, RRU8<3, 49, 20>>
182/// rint!(3..50, default = 20, bits = 6) // RInt<u7, RRU8<3, 49, 20>>
183/// ```
184#[proc_macro]
185pub fn rint(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
186    unwrap_stream(rint::rint(item))
187}
188
189fn unwrap_stream(stream: syn::Result<proc_macro2::TokenStream>) -> proc_macro::TokenStream {
190    match stream {
191        Ok(stream) => stream,
192        Err(err) => err.into_compile_error(),
193    }
194    .into()
195}
196
197/// must 1~128
198fn valid_int_bits(bits: &[u8]) -> Option<NonZero<u8>> {
199    let bits = match bits {
200        [ss @ b'1'..=b'9'] => ss - b'0',
201        [fs @ b'1'..=b'9', ss @ b'0'..=b'9'] => (fs - b'0') * 10 + (ss - b'0'),
202        [b'1', fs @ b'0'..=b'1', ss @ b'0'..=b'9'] => 100 + (fs - b'0') * 10 + (ss - b'0'),
203        [b'1', b'2', ss @ b'0'..=b'8'] => 120 + (ss - b'0'),
204        _ => 0,
205    };
206    NonZero::new(bits)
207}
208
209/// u1~u128
210fn valid_uint(repr: &syn::Ident) -> syn::Result<u32> {
211    if let [b'u', bits @ ..] = repr.to_string().as_bytes()
212        && let Some(bits) = valid_int_bits(bits)
213    {
214        Ok(bits.get() as u32)
215    } else {
216        error(repr.span(), "must be unsigned integer type")
217    }
218}
219
220/// u1~u128 or i1~i128
221fn valid_int(repr: &syn::Ident) -> syn::Result<(bool, u32)> {
222    if let [sign @ b'u' | sign @ b'i', bits @ ..] = repr.to_string().as_bytes()
223        && let Some(bits) = valid_int_bits(bits)
224    {
225        Ok((*sign == b'i', bits.get() as u32))
226    } else {
227        error(repr.span(), "must be integer type")
228    }
229}
230
231mod bitfld;
232mod iter_enum;
233mod map_enum;
234mod rint;