@@ -9,20 +9,22 @@ use crate::interpreter::ExecResult;
99
1010/// The tree builtin command.
1111///
12- /// Usage: tree [-a] [-d] [-L level] [-I pattern] [PATH...]
12+ /// Usage: tree [-a] [-d] [-L level] [-I pattern] [--noreport] [ PATH...]
1313///
1414/// Options:
15- /// -a Show hidden files
16- /// -d Directories only
17- /// -L level Limit depth to level
18- /// -I pattern Exclude files matching pattern
15+ /// -a Show hidden files
16+ /// -d Directories only
17+ /// -L level Limit depth to level
18+ /// -I pattern Exclude files matching pattern
19+ /// --noreport Suppress directory/file count report
1920pub struct Tree ;
2021
2122struct TreeOptions {
2223 show_hidden : bool ,
2324 dirs_only : bool ,
2425 max_depth : Option < usize > ,
2526 exclude_pattern : Option < String > ,
27+ noreport : bool ,
2628}
2729
2830struct TreeCounts {
@@ -38,6 +40,7 @@ impl Builtin for Tree {
3840 dirs_only : false ,
3941 max_depth : None ,
4042 exclude_pattern : None ,
43+ noreport : false ,
4144 } ;
4245
4346 let mut paths: Vec < & str > = Vec :: new ( ) ;
@@ -60,11 +63,25 @@ impl Builtin for Tree {
6063 } else if let Some ( val) = p. flag_value_opt ( "-I" ) {
6164 opts. exclude_pattern = Some ( val. to_string ( ) ) ;
6265 } else if p. is_flag ( ) {
63- // Handle combined flags like -ad
6466 let Some ( s) = p. current ( ) else {
6567 p. advance ( ) ;
6668 continue ;
6769 } ;
70+ // Handle long options (--foo) before short-flag loop
71+ if s. starts_with ( "--" ) {
72+ match s {
73+ "--noreport" => opts. noreport = true ,
74+ _ => {
75+ return Ok ( ExecResult :: err (
76+ format ! ( "tree: unrecognized option '{}'\n " , s) ,
77+ 1 ,
78+ ) ) ;
79+ }
80+ }
81+ p. advance ( ) ;
82+ continue ;
83+ }
84+ // Handle combined short flags like -ad
6885 for ch in s[ 1 ..] . chars ( ) {
6986 match ch {
7087 'a' => opts. show_hidden = true ,
@@ -108,20 +125,22 @@ impl Builtin for Tree {
108125 let mut counts = TreeCounts { dirs : 0 , files : 0 } ;
109126 build_tree ( & ctx, & root, "" , & opts, 0 , & mut counts, & mut output) . await ;
110127
111- if opts. dirs_only {
112- output. push_str ( & format ! (
113- "\n {} director{}\n " ,
114- counts. dirs,
115- if counts. dirs == 1 { "y" } else { "ies" }
116- ) ) ;
117- } else {
118- output. push_str ( & format ! (
119- "\n {} director{}, {} file{}\n " ,
120- counts. dirs,
121- if counts. dirs == 1 { "y" } else { "ies" } ,
122- counts. files,
123- if counts. files == 1 { "" } else { "s" }
124- ) ) ;
128+ if !opts. noreport {
129+ if opts. dirs_only {
130+ output. push_str ( & format ! (
131+ "\n {} director{}\n " ,
132+ counts. dirs,
133+ if counts. dirs == 1 { "y" } else { "ies" }
134+ ) ) ;
135+ } else {
136+ output. push_str ( & format ! (
137+ "\n {} director{}, {} file{}\n " ,
138+ counts. dirs,
139+ if counts. dirs == 1 { "y" } else { "ies" } ,
140+ counts. files,
141+ if counts. files == 1 { "" } else { "s" }
142+ ) ) ;
143+ }
125144 }
126145 }
127146
@@ -381,4 +400,25 @@ mod tests {
381400 assert_eq ! ( result. exit_code, 1 ) ;
382401 assert ! ( result. stderr. contains( "invalid option" ) ) ;
383402 }
403+
404+ #[ tokio:: test]
405+ async fn test_tree_noreport ( ) {
406+ let fs = setup_fs ( ) . await ;
407+ let result = run_tree ( & [ "--noreport" , "/project" ] , fs) . await ;
408+ assert_eq ! ( result. exit_code, 0 ) ;
409+ assert ! ( result. stdout. contains( "/project" ) ) ;
410+ assert ! ( result. stdout. contains( "src" ) ) ;
411+ assert ! ( result. stdout. contains( "Cargo.toml" ) ) ;
412+ // --noreport should suppress the summary line
413+ assert ! ( !result. stdout. contains( "director" ) ) ;
414+ assert ! ( !result. stdout. contains( "file" ) ) ;
415+ }
416+
417+ #[ tokio:: test]
418+ async fn test_tree_unknown_long_option ( ) {
419+ let fs = Arc :: new ( InMemoryFs :: new ( ) ) as Arc < dyn FileSystem > ;
420+ let result = run_tree ( & [ "--bogus" ] , fs) . await ;
421+ assert_eq ! ( result. exit_code, 1 ) ;
422+ assert ! ( result. stderr. contains( "unrecognized option" ) ) ;
423+ }
384424}
0 commit comments