@@ -13,14 +13,16 @@ pub struct Manifest {
1313 pub version : String ,
1414 pub paths : BTreeMap < String , String > ,
1515 pub dependencies : BTreeMap < String , Vec < String > > ,
16+ #[ serde( skip) ]
17+ pub ext_wrappers : BTreeMap < String , Vec < String > > ,
1618}
1719
1820const SCHEMA_VERSION : u32 = 1 ;
1921
2022impl Manifest {
2123 pub fn load ( path : & Path ) -> Result < Self > {
2224 let raw = std:: fs:: read_to_string ( path) . with_context ( || format ! ( "reading manifest at {}" , path. display( ) ) ) ?;
23- let m: Manifest = serde_json:: from_str ( & raw ) . with_context ( || format ! ( "parsing manifest at {}" , path. display( ) ) ) ?;
25+ let mut m: Manifest = serde_json:: from_str ( & raw ) . with_context ( || format ! ( "parsing manifest at {}" , path. display( ) ) ) ?;
2426
2527 if m. schema_version != SCHEMA_VERSION {
2628 return Err ( anyhow ! (
@@ -31,6 +33,10 @@ impl Manifest {
3133 ) ) ;
3234 }
3335
36+ if let Some ( bindings_root) = path. parent ( ) {
37+ m. ext_wrappers = load_ext_wrappers ( bindings_root) . unwrap_or_default ( ) ;
38+ }
39+
3440 Ok ( m)
3541 }
3642
@@ -63,3 +69,81 @@ impl Manifest {
6369 out
6470 }
6571}
72+
73+ fn load_ext_wrappers ( bindings_root : & Path ) -> Option < BTreeMap < String , Vec < String > > > {
74+ let ext_path = bindings_root. join ( "src" ) . join ( "ext.rs" ) ;
75+ let text = std:: fs:: read_to_string ( & ext_path) . ok ( ) ?;
76+ let file = syn:: parse_file ( & text) . ok ( ) ?;
77+
78+ let mut out: BTreeMap < String , Vec < String > > = BTreeMap :: new ( ) ;
79+
80+ for item in & file. items {
81+ let syn:: Item :: Mod ( m) = item else { continue } ;
82+ let features = extract_cfg_features ( & m. attrs ) ;
83+ if features. is_empty ( ) {
84+ continue ;
85+ }
86+
87+ let Some ( ( _, inner_items) ) = m. content . as_ref ( ) else { continue } ;
88+
89+ for inner in inner_items {
90+ let ( name, is_pub) = match inner {
91+ syn:: Item :: Struct ( s) => ( s. ident . to_string ( ) , is_public ( & s. vis ) ) ,
92+ syn:: Item :: Trait ( t) => ( t. ident . to_string ( ) , is_public ( & t. vis ) ) ,
93+ syn:: Item :: Fn ( f) => ( f. sig . ident . to_string ( ) , is_public ( & f. vis ) ) ,
94+ _ => continue ,
95+ } ;
96+
97+ if !is_pub {
98+ continue ;
99+ }
100+
101+ out. insert ( name, features. clone ( ) ) ;
102+ }
103+ }
104+
105+ Some ( out)
106+ }
107+
108+ fn is_public ( vis : & syn:: Visibility ) -> bool {
109+ matches ! ( vis, syn:: Visibility :: Public ( _) )
110+ }
111+
112+ fn extract_cfg_features ( attrs : & [ syn:: Attribute ] ) -> Vec < String > {
113+ for attr in attrs {
114+ if !attr. path ( ) . is_ident ( "cfg" ) {
115+ continue ;
116+ }
117+ if let Ok ( meta) = attr. parse_args :: < syn:: Meta > ( ) {
118+ return collect_features ( & meta) ;
119+ }
120+ }
121+ Vec :: new ( )
122+ }
123+
124+ fn collect_features ( meta : & syn:: Meta ) -> Vec < String > {
125+ match meta {
126+ syn:: Meta :: NameValue ( nv) if nv. path . is_ident ( "feature" ) => {
127+ if let syn:: Expr :: Lit ( syn:: ExprLit {
128+ lit : syn:: Lit :: Str ( s) ,
129+ ..
130+ } ) = & nv. value
131+ {
132+ vec ! [ s. value( ) ]
133+ } else {
134+ Vec :: new ( )
135+ }
136+ }
137+ syn:: Meta :: List ( list) if list. path . is_ident ( "all" ) => {
138+ let parsed: syn:: punctuated:: Punctuated < syn:: Meta , syn:: Token ![ , ] > = list
139+ . parse_args_with ( syn:: punctuated:: Punctuated :: parse_terminated)
140+ . unwrap_or_default ( ) ;
141+ let mut out = Vec :: new ( ) ;
142+ for inner in parsed {
143+ out. extend ( collect_features ( & inner) ) ;
144+ }
145+ out
146+ }
147+ _ => Vec :: new ( ) ,
148+ }
149+ }
0 commit comments