@@ -6,6 +6,7 @@ use crate::{
66} ;
77use anyhow:: Result ;
88use heck:: * ;
9+ use indexmap:: IndexSet ;
910use std:: collections:: { BTreeMap , BTreeSet } ;
1011use std:: fmt:: Write as _;
1112use std:: mem;
@@ -2086,6 +2087,119 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
20862087 result
20872088 }
20882089
2090+ /// The owning interface and its package, when `id` is owned by an interface.
2091+ fn type_owner ( & self , id : TypeId ) -> Option < ( InterfaceId , & PackageName ) > {
2092+ let TypeOwner :: Interface ( iface_id) = self . resolve . types [ id] . owner else {
2093+ return None ;
2094+ } ;
2095+ let pkg_id = self . resolve . interfaces [ iface_id] . package ?;
2096+ Some ( ( iface_id, & self . resolve . packages [ pkg_id] . name ) )
2097+ }
2098+
2099+ /// Selector keys naming the type itself: its bare wit name and its
2100+ /// fully-qualified `ns:pkg/iface/type` name. Qualified keys are written exactly
2101+ /// as in `with`, so they carry the package `@version` when versioned.
2102+ fn type_identity_keys ( & self , id : TypeId ) -> Vec < String > {
2103+ let id = dealias ( self . resolve , id) ;
2104+ match self . resolve . types [ id] . name . as_deref ( ) {
2105+ Some ( name) => vec ! [ name. to_string( ) , full_wit_type_name( self . resolve, id) ] ,
2106+ None => Vec :: new ( ) ,
2107+ }
2108+ }
2109+
2110+ /// Every selector key that matches type `id`: its identity keys plus its
2111+ /// owning interface and package.
2112+ fn type_selector_keys ( & self , id : TypeId ) -> Vec < String > {
2113+ let id = dealias ( self . resolve , id) ;
2114+ let mut keys = self . type_identity_keys ( id) ;
2115+ if let Some ( ( iface_id, pkg) ) = self . type_owner ( id) {
2116+ keys. push ( pkg. to_string ( ) ) ;
2117+ if let Some ( iface) = self . resolve . id_of ( iface_id) {
2118+ keys. push ( iface) ;
2119+ }
2120+ }
2121+ keys
2122+ }
2123+
2124+ /// The attributes from `entries` whose selector satisfies `matches`, deduped in
2125+ /// configured order, paired with the distinct selectors that matched. The
2126+ /// matched selectors are recorded so `finish` can report selectors that matched
2127+ /// nothing, exactly as the `with` option reports unused remappings.
2128+ fn matching_attrs (
2129+ entries : & [ ( String , String ) ] ,
2130+ matches : impl Fn ( & str ) -> bool ,
2131+ ) -> ( Vec < String > , Vec < String > ) {
2132+ let mut attrs = IndexSet :: new ( ) ;
2133+ let mut used = IndexSet :: new ( ) ;
2134+ for ( sel, attr) in entries. iter ( ) . filter ( |( sel, _) | matches ( sel) ) {
2135+ attrs. insert ( attr. clone ( ) ) ;
2136+ used. insert ( sel. clone ( ) ) ;
2137+ }
2138+ ( attrs. into_iter ( ) . collect ( ) , used. into_iter ( ) . collect ( ) )
2139+ }
2140+
2141+ /// Attributes configured via `additional_type_attributes` for type `id`.
2142+ fn additional_type_attrs ( & mut self , id : TypeId ) -> Vec < String > {
2143+ let keys = self . type_selector_keys ( id) ;
2144+ let ( attrs, used) =
2145+ Self :: matching_attrs ( & self . r#gen . opts . additional_type_attributes , |sel| {
2146+ keys. iter ( ) . any ( |k| k == sel)
2147+ } ) ;
2148+ self . r#gen . used_type_attr_selectors . extend ( used) ;
2149+ attrs
2150+ }
2151+
2152+ /// Attributes configured via `additional_member_attributes` for `member` (a
2153+ /// record field or enum/variant case) of type `id`, matched by `<type>.<member>`
2154+ /// for any type selector (bare, qualified, interface, or package) or a bare
2155+ /// `<member>`.
2156+ fn additional_member_attrs ( & mut self , id : TypeId , member : & str ) -> Vec < String > {
2157+ let type_keys = self . type_selector_keys ( id) ;
2158+ let ( attrs, used) =
2159+ Self :: matching_attrs ( & self . r#gen . opts . additional_member_attributes , |sel| {
2160+ // member names are dot-free, so the last `.` splits `<type>.<member>`
2161+ sel == member
2162+ || sel
2163+ . rsplit_once ( '.' )
2164+ . is_some_and ( |( ty, m) | m == member && type_keys. iter ( ) . any ( |tk| tk == ty) )
2165+ } ) ;
2166+ self . r#gen . used_member_attr_selectors . extend ( used) ;
2167+ attrs
2168+ }
2169+
2170+ /// Split injected attributes into derive paths and everything else. Derive
2171+ /// paths are merged into the generated `#[derive(...)]` so they dedup against
2172+ /// the built-in and `additional_derives` derives instead of colliding (E0119);
2173+ /// other attributes are emitted verbatim.
2174+ fn split_injected_derives ( attrs : & [ String ] ) -> ( Vec < String > , Vec < String > ) {
2175+ let mut derives = Vec :: new ( ) ;
2176+ let mut others = Vec :: new ( ) ;
2177+ for attr in attrs {
2178+ match attr
2179+ . trim ( )
2180+ . strip_prefix ( "#[derive(" )
2181+ . and_then ( |s| s. strip_suffix ( ")]" ) )
2182+ {
2183+ Some ( inner) => derives. extend (
2184+ inner
2185+ . split ( ',' )
2186+ . map ( str:: trim)
2187+ . filter ( |p| !p. is_empty ( ) )
2188+ . map ( String :: from) ,
2189+ ) ,
2190+ None => others. push ( attr. clone ( ) ) ,
2191+ }
2192+ }
2193+ ( derives, others)
2194+ }
2195+
2196+ /// Emit each attribute on its own line, verbatim.
2197+ fn push_attrs ( & mut self , attrs : & [ String ] ) {
2198+ for attr in attrs {
2199+ uwriteln ! ( self . src, "{attr}" ) ;
2200+ }
2201+ }
2202+
20892203 fn print_typedef_record ( & mut self , id : TypeId , record : & Record , docs : & Docs ) {
20902204 let info = self . info ( id) ;
20912205 // We use a BTree set to make sure we don't have any duplicates and we have a stable order
@@ -2096,6 +2210,14 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
20962210 . iter ( )
20972211 . cloned ( )
20982212 . collect ( ) ;
2213+ // Computed once, then emitted on every ownership mode below.
2214+ let ( injected_derives, injected_attrs) =
2215+ Self :: split_injected_derives ( & self . additional_type_attrs ( id) ) ;
2216+ let field_attrs: Vec < Vec < String > > = record
2217+ . fields
2218+ . iter ( )
2219+ . map ( |f| self . additional_member_attrs ( id, & f. name ) )
2220+ . collect ( ) ;
20992221 for ( name, mode) in self . modes_of ( id) {
21002222 self . rustdoc ( docs) ;
21012223 let mut derives = BTreeSet :: new ( ) ;
@@ -2113,16 +2235,19 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
21132235 } else if info. is_clone ( ) {
21142236 derives. insert ( "Clone" . to_string ( ) ) ;
21152237 }
2238+ derives. extend ( injected_derives. iter ( ) . cloned ( ) ) ;
21162239 if !derives. is_empty ( ) {
21172240 self . push_str ( "#[derive(" ) ;
21182241 self . push_str ( & derives. into_iter ( ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ) ;
21192242 self . push_str ( ")]\n " )
21202243 }
2244+ self . push_attrs ( & injected_attrs) ;
21212245 self . push_str ( & format ! ( "pub struct {name}" ) ) ;
21222246 self . print_generics ( mode. lifetime ) ;
21232247 self . push_str ( " {\n " ) ;
2124- for field in record. fields . iter ( ) {
2248+ for ( field, attrs ) in record. fields . iter ( ) . zip ( & field_attrs ) {
21252249 self . rustdoc ( & field. docs ) ;
2250+ self . push_attrs ( attrs) ;
21262251 self . push_str ( "pub " ) ;
21272252 self . push_str ( & to_rust_ident ( & field. name ) ) ;
21282253 self . push_str ( ": " ) ;
@@ -2182,10 +2307,12 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
21822307 {
21832308 self . print_rust_enum (
21842309 id,
2310+ // Raw wit case names; `print_rust_enum` upper-camel-cases them at the
2311+ // emit site so member selectors still match the wit (kebab) name.
21852312 variant
21862313 . cases
21872314 . iter ( )
2188- . map ( |c| ( c. name . to_upper_camel_case ( ) , & c. docs , c. ty . as_ref ( ) ) ) ,
2315+ . map ( |c| ( c. name . clone ( ) , & c. docs , c. ty . as_ref ( ) ) ) ,
21892316 docs,
21902317 ) ;
21912318 }
@@ -2207,6 +2334,13 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
22072334 . iter ( )
22082335 . cloned ( )
22092336 . collect ( ) ;
2337+ let ( injected_derives, injected_attrs) =
2338+ Self :: split_injected_derives ( & self . additional_type_attrs ( id) ) ;
2339+ let case_attrs: Vec < Vec < String > > = cases
2340+ . clone ( )
2341+ . into_iter ( )
2342+ . map ( |( case_name, _, _) | self . additional_member_attrs ( id, & case_name) )
2343+ . collect ( ) ;
22102344 for ( name, mode) in self . modes_of ( id) {
22112345 self . rustdoc ( docs) ;
22122346 let mut derives = BTreeSet :: new ( ) ;
@@ -2223,17 +2357,20 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
22232357 } else if info. is_clone ( ) {
22242358 derives. insert ( "Clone" . to_string ( ) ) ;
22252359 }
2360+ derives. extend ( injected_derives. iter ( ) . cloned ( ) ) ;
22262361 if !derives. is_empty ( ) {
22272362 self . push_str ( "#[derive(" ) ;
22282363 self . push_str ( & derives. into_iter ( ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ) ;
22292364 self . push_str ( ")]\n " )
22302365 }
2366+ self . push_attrs ( & injected_attrs) ;
22312367 self . push_str ( & format ! ( "pub enum {name}" ) ) ;
22322368 self . print_generics ( mode. lifetime ) ;
22332369 self . push_str ( " {\n " ) ;
2234- for ( case_name, docs, payload) in cases. clone ( ) {
2370+ for ( ( case_name, docs, payload) , attrs ) in cases. clone ( ) . into_iter ( ) . zip ( & case_attrs ) {
22352371 self . rustdoc ( docs) ;
2236- self . push_str ( & case_name) ;
2372+ self . push_attrs ( attrs) ;
2373+ self . push_str ( & case_name. to_upper_camel_case ( ) ) ;
22372374 if let Some ( ty) = payload {
22382375 self . push_str ( "(" ) ;
22392376 let mode = self . filter_mode ( ty, mode) ;
@@ -2250,7 +2387,7 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
22502387 cases
22512388 . clone ( )
22522389 . into_iter ( )
2253- . map ( |( name, _docs, ty) | ( name, ty) ) ,
2390+ . map ( |( name, _docs, ty) | ( name. to_upper_camel_case ( ) , ty) ) ,
22542391 ) ;
22552392
22562393 if info. error {
@@ -2343,24 +2480,14 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
23432480 }
23442481 }
23452482
2346- fn print_typedef_enum (
2347- & mut self ,
2348- id : TypeId ,
2349- name : & str ,
2350- enum_ : & Enum ,
2351- docs : & Docs ,
2352- attrs : & [ String ] ,
2353- case_attr : Box < dyn Fn ( & EnumCase ) -> String > ,
2354- ) where
2355- Self : Sized ,
2356- {
2483+ fn print_typedef_enum ( & mut self , id : TypeId , name : & str , enum_ : & Enum , docs : & Docs ) {
23572484 let info = self . info ( id) ;
23582485
23592486 let name = to_upper_camel_case ( name) ;
23602487 self . rustdoc ( docs) ;
2361- for attr in attrs {
2362- self . push_str ( & format ! ( "{attr} \n " ) ) ;
2363- }
2488+ let ( injected_derives , injected_attrs ) =
2489+ Self :: split_injected_derives ( & self . additional_type_attrs ( id ) ) ;
2490+ self . push_attrs ( & injected_attrs ) ;
23642491 self . push_str ( "#[repr(" ) ;
23652492 self . int_repr ( enum_. tag ( ) ) ;
23662493 self . push_str ( ")]\n " ) ;
@@ -2379,13 +2506,15 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
23792506 . into_iter ( )
23802507 . map ( |s| s. to_string ( ) ) ,
23812508 ) ;
2509+ derives. extend ( injected_derives) ;
23822510 self . push_str ( "#[derive(" ) ;
23832511 self . push_str ( & derives. into_iter ( ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ) ;
23842512 self . push_str ( ")]\n " ) ;
23852513 self . push_str ( & format ! ( "pub enum {name} {{\n " ) ) ;
23862514 for case in enum_. cases . iter ( ) {
23872515 self . rustdoc ( & case. docs ) ;
2388- self . push_str ( & case_attr ( case) ) ;
2516+ let case_attrs = self . additional_member_attrs ( id, & case. name ) ;
2517+ self . push_attrs ( & case_attrs) ;
23892518 self . push_str ( & case. name . to_upper_camel_case ( ) ) ;
23902519 self . push_str ( ",\n " ) ;
23912520 }
@@ -2967,7 +3096,7 @@ impl<'a> {camel}Borrow<'a>{{
29673096 }
29683097
29693098 fn type_enum ( & mut self , id : TypeId , name : & str , enum_ : & Enum , docs : & Docs ) {
2970- self . print_typedef_enum ( id, name, enum_, docs, & [ ] , Box :: new ( |_| String :: new ( ) ) ) ;
3099+ self . print_typedef_enum ( id, name, enum_, docs) ;
29713100
29723101 let name = to_upper_camel_case ( name) ;
29733102 let mut cases = String :: new ( ) ;
0 commit comments