@@ -82,7 +82,35 @@ static PATH_MAPPINGS: Map<&'static str, &'static str> = phf_map! {
8282const PATH_RESOLVER : PathResolver < & ' static Map < & ' static str , & ' static str > > =
8383 create_static_resolver ( & PATH_MAPPINGS , true ) ;
8484
85- /// Implementation of `#[butane::model]`.
85+ /// Core implementation shared by both `#[model]` attribute macro and `#[derive(DataObject)]` derive macro.
86+ ///
87+ /// Generates the `DataObject` trait implementation and field expression helpers.
88+ /// Also writes migration information to disk.
89+ ///
90+ /// Returns a tuple of (trait implementations, field expressions).
91+ fn dataobject_impl < M > (
92+ ast_struct : & ItemStruct ,
93+ ms : & mut impl MigrationsMut < M = M > ,
94+ ) -> ( TokenStream2 , TokenStream2 )
95+ where
96+ M : MigrationMut ,
97+ {
98+ let config: dbobj:: Config = config_from_attributes ( ast_struct) ;
99+ migration:: write_table_to_disk ( ms, ast_struct, & config) . unwrap ( ) ;
100+ let impltraits = dbobj:: impl_dbobject ( ast_struct, & config) ;
101+ let fieldexprs = dbobj:: add_fieldexprs ( ast_struct, & config) ;
102+ ( impltraits, fieldexprs)
103+ }
104+
105+ /// Implementation of the `#[model]` attribute macro.
106+ ///
107+ /// This attribute macro transforms a struct into a database model by:
108+ /// 1. Generating `DataObject` trait implementations
109+ /// 2. Generating field expression helpers
110+ /// 3. Recording migration information
111+ /// 4. Re-emitting the struct definition with helper attributes removed
112+ ///
113+ /// The macro accepts helper attributes like `#[table]`, `#[pk]`, `#[unique]`, etc.
86114pub fn model_with_migrations < M > (
87115 input : TokenStream2 ,
88116 ms : & mut impl MigrationsMut < M = M > ,
@@ -94,17 +122,12 @@ where
94122 // attributes but proc macro attributes can't yet (nor can they
95123 // create field attributes)
96124 let mut ast_struct: ItemStruct = syn:: parse2 ( input) . unwrap ( ) ;
97- let config: dbobj:: Config = config_from_attributes ( & ast_struct) ;
98125
99126 // Filter out our helper attributes
100127 let attrs: Vec < Attribute > = filter_helper_attributes ( & ast_struct) ;
101-
102128 let vis = & ast_struct. vis ;
103129
104- migration:: write_table_to_disk ( ms, & ast_struct, & config) . unwrap ( ) ;
105-
106- let impltraits = dbobj:: impl_dbobject ( & ast_struct, & config) ;
107- let fieldexprs = dbobj:: add_fieldexprs ( & ast_struct, & config) ;
130+ let ( impltraits, fieldexprs) = dataobject_impl ( & ast_struct, ms) ;
108131
109132 let fields: Punctuated < Field , syn:: token:: Comma > =
110133 match remove_helper_field_attributes ( & mut ast_struct. fields ) {
@@ -124,6 +147,31 @@ where
124147 )
125148}
126149
150+ /// Implementation of the `#[derive(DataObject)]` derive macro.
151+ ///
152+ /// This derive macro generates the same `DataObject` trait implementations and helpers
153+ /// as the `#[model]` attribute macro, but follows derive macro conventions:
154+ /// - Only generates additional code (trait impls, helpers)
155+ /// - Does NOT re-emit the struct definition
156+ /// - Requires the struct to already exist with all fields defined
157+ ///
158+ /// Functionally equivalent to `#[model]` but with derive syntax.
159+ pub fn derive_dataobject_with_migrations < M > (
160+ input : TokenStream2 ,
161+ ms : & mut impl MigrationsMut < M = M > ,
162+ ) -> TokenStream2
163+ where
164+ M : MigrationMut ,
165+ {
166+ let ast_struct: ItemStruct = syn:: parse2 ( input) . unwrap ( ) ;
167+ let ( impltraits, fieldexprs) = dataobject_impl ( & ast_struct, ms) ;
168+
169+ quote ! (
170+ #impltraits
171+ #fieldexprs
172+ )
173+ }
174+
127175/// Implementation of `#[butane::dataresult(<Model>)]`.
128176pub fn dataresult ( args : TokenStream2 , input : TokenStream2 ) -> TokenStream2 {
129177 let dbo: Ident = syn:: parse2 ( args)
0 commit comments