@@ -129,6 +129,34 @@ pub fn bitmap(input: TokenStream) -> TokenStream {
129129 }
130130}
131131
132+ /// Generates a packed bitmap newtype struct with field-level bit access.
133+ ///
134+ /// This attribute macro works exactly like the `bitmap!` macro but uses attribute syntax.
135+ ///
136+ /// ### Usage Example
137+ /// ```
138+ /// use bitmap::bitmap_attr;
139+ ///
140+ /// #[bitmap_attr]
141+ /// struct Player {
142+ /// imposter: u1,
143+ /// finished_tasks: u3,
144+ /// kills: u3,
145+ /// }
146+ ///
147+ /// let mut player = Player(0);
148+ /// assert_eq!(std::mem::size_of::<Player>(), 1);
149+ ///
150+ /// player.set_imposter(1);
151+ /// player.set_finished_tasks(5);
152+ /// player.set_kills(3);
153+ ///
154+ /// assert_eq!(player.imposter(), 1);
155+ /// assert_eq!(player.finished_tasks(), 5);
156+ /// assert_eq!(player.kills(), 3);
157+ /// assert_eq!(*player, 0b01101011);
158+ /// ```
159+
132160#[ proc_macro_attribute]
133161pub fn bitmap_attr ( _args : TokenStream , input : TokenStream ) -> TokenStream {
134162 let input = parse_macro_input ! ( input as DeriveInput ) ;
@@ -142,7 +170,7 @@ pub fn bitmap_attr(_args: TokenStream, input: TokenStream) -> TokenStream {
142170}
143171
144172fn convert_derive_to_bitmap_input ( input : DeriveInput ) -> parser:: BitmapInput {
145- let name = input. ident ;
173+ let name = input. ident ;
146174
147175 let syn:: Data :: Struct ( data_struct) = input. data else {
148176 panic ! ( "#[bitmap_attr] can only be used on structs" ) ;
@@ -156,17 +184,12 @@ fn convert_derive_to_bitmap_input(input: DeriveInput) -> parser::BitmapInput {
156184 let field_name = field. ident . expect ( "Field must have a name" ) ;
157185 let field_type = field. ty ;
158186
159- let type_str = field_type. to_token_stream ( ) . to_string ( ) ;
160-
161- if !type_str. starts_with ( "u" ) {
162- panic ! ( "Field type must be unsigned integer like u1, u2, etc." ) ;
163- }
187+ let syn:: Type :: Path ( type_path) = field_type else {
188+ panic ! ( "Field type must be a path like u1, u7, etc." ) ;
189+ } ;
164190
165- let size: u8 = type_str[ 1 ..] . parse ( ) . expect ( "Invalid bit width" ) ;
166-
167- if size == 0 || size > 128 {
168- panic ! ( "Invalid size for {}, expected u{{1..128}}" , type_str) ;
169- }
191+ let segment = type_path. path . segments . last ( ) . expect ( "Type path must have a segment" ) ;
192+ let size = parser:: parse_bit_width ( & segment. ident ) . expect ( "Invalid bit width" ) ;
170193
171194 parser:: FieldDef {
172195 name : field_name,
@@ -175,7 +198,7 @@ fn convert_derive_to_bitmap_input(input: DeriveInput) -> parser::BitmapInput {
175198 } ) . collect ( ) ;
176199
177200 parser:: BitmapInput {
178- name,
201+ name,
179202 fields,
180203 }
181204}
0 commit comments