@@ -5,7 +5,6 @@ use std::path::Path;
55use tar:: Archive ;
66use flate2:: read:: GzDecoder ;
77use syn:: { File , Item } ;
8- use tokio:: fs as tokio_fs;
98
109/// Represents filters for item listing.
1110#[ derive( Debug ) ]
@@ -46,24 +45,65 @@ pub async fn list_crate_items(
4645 let crate_path = download_and_cache_crate ( crate_name, version) . await ?;
4746 let mut items = Vec :: new ( ) ;
4847
49- for entry in fs:: read_dir ( crate_path) ? {
50- let entry = entry?;
51- let path = entry. path ( ) ;
52- if path. extension ( ) . and_then ( |ext| ext. to_str ( ) ) == Some ( "rs" ) {
53- let content = fs:: read_to_string ( & path) ?;
54- let parsed_file: File = syn:: parse_file ( & content) ?;
48+ // Most crates have their source in a "src" subdirectory
49+ let src_path = Path :: new ( & crate_path) . join ( "src" ) ;
5550
56- for item in parsed_file. items {
57- match item {
58- Item :: Struct ( _) if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) == Some ( "struct" ) ) => items. push ( format ! ( "{:?}" , item) ) ,
59- Item :: Enum ( _) if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) == Some ( "enum" ) ) => items. push ( format ! ( "{:?}" , item) ) ,
60- Item :: Trait ( _) if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) == Some ( "trait" ) ) => items. push ( format ! ( "{:?}" , item) ) ,
61- Item :: Fn ( _) if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) == Some ( "fn" ) ) => items. push ( format ! ( "{:?}" , item) ) ,
62- _ => { }
51+ fn visit_rs_files < F : FnMut ( & Path ) > ( dir : & Path , cb : & mut F ) {
52+ if let Ok ( entries) = fs:: read_dir ( dir) {
53+ for entry in entries. flatten ( ) {
54+ let path = entry. path ( ) ;
55+ if path. is_dir ( ) {
56+ visit_rs_files ( & path, cb) ;
57+ } else if path. extension ( ) . and_then ( |ext| ext. to_str ( ) ) == Some ( "rs" ) {
58+ cb ( & path) ;
6359 }
6460 }
6561 }
6662 }
6763
68- Ok ( items. join ( "\n " ) )
64+ visit_rs_files ( & src_path, & mut |path : & Path | {
65+ if let Ok ( content) = fs:: read_to_string ( path) {
66+ if let Ok ( parsed_file) = syn:: parse_file ( & content) {
67+ for item in parsed_file. items {
68+ if let Item :: Struct ( s) = & item {
69+ if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) . map_or ( true , |ty| ty == "struct" ) ) {
70+ items. push ( ( "Structs" , format ! ( "{}" , s. ident) ) ) ;
71+ }
72+ }
73+ if let Item :: Enum ( e) = & item {
74+ if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) . map_or ( true , |ty| ty == "enum" ) ) {
75+ items. push ( ( "Enums" , format ! ( "{}" , e. ident) ) ) ;
76+ }
77+ }
78+ if let Item :: Trait ( t) = & item {
79+ if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) . map_or ( true , |ty| ty == "trait" ) ) {
80+ items. push ( ( "Traits" , format ! ( "{}" , t. ident) ) ) ;
81+ }
82+ }
83+ if let Item :: Fn ( f) = & item {
84+ if filters. as_ref ( ) . map_or ( true , |f| f. item_type . as_deref ( ) . map_or ( true , |ty| ty == "fn" ) ) {
85+ items. push ( ( "Functions" , format ! ( "{}" , f. sig. ident) ) ) ;
86+ }
87+ }
88+ }
89+ }
90+ }
91+ } ) ;
92+
93+ use std:: collections:: BTreeMap ;
94+ let mut grouped: BTreeMap < & str , Vec < String > > = BTreeMap :: new ( ) ;
95+ for ( kind, name) in items {
96+ grouped. entry ( kind) . or_default ( ) . push ( name) ;
97+ }
98+
99+ let mut output = String :: new ( ) ;
100+ for ( kind, names) in grouped {
101+ output. push_str ( & format ! ( "## {}\n " , kind) ) ;
102+ for name in names {
103+ output. push_str ( & format ! ( "- {}\n " , name) ) ;
104+ }
105+ output. push ( '\n' ) ;
106+ }
107+
108+ Ok ( output)
69109}
0 commit comments