@@ -11,14 +11,26 @@ use crate::{println_nopipe, RunnableCmd};
1111use super :: cmd_version:: built_info;
1212use anyhow:: Result ;
1313use async_trait:: async_trait;
14- use clap:: { Command , Parser } ;
14+ use clap:: { Command , Parser , ValueEnum } ;
1515use serde:: Serialize ;
1616
17- /// Generate CLI docs in JSON format
17+ /// List all CLI commands
1818#[ derive( Parser , Debug , Clone ) ]
1919#[ command( verbatim_doc_comment) ]
2020#[ command( name = "docs" ) ]
21- pub struct CmdDocs ;
21+ pub struct CmdDocs {
22+ /// Output format
23+ #[ clap( long, default_value = "text" ) ]
24+ format : DocsFormat ,
25+ }
26+
27+ #[ derive( ValueEnum , Debug , Clone ) ]
28+ enum DocsFormat {
29+ /// Command and description
30+ Text ,
31+ /// Full JSON tree with args and metadata
32+ Json ,
33+ }
2234
2335/// Arg to CLI command for the JSON doc
2436#[ derive( Serialize , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -105,15 +117,51 @@ fn to_json(cmd: &Command) -> JsonDoc {
105117 }
106118}
107119
120+ fn print_flat ( cmd : & Command , prefix : & str ) {
121+ let name = if prefix. is_empty ( ) {
122+ cmd. get_name ( ) . to_string ( )
123+ } else {
124+ format ! ( "{} {}" , prefix, cmd. get_name( ) )
125+ } ;
126+
127+ let mut subs: Vec < _ > = cmd
128+ . get_subcommands ( )
129+ . filter ( |c| c. get_name ( ) != "help" )
130+ . collect ( ) ;
131+
132+ // Print this command if it's a leaf or if it's runnable on its own
133+ // (i.e., subcommands exist but aren't required, like `oxide disk import`)
134+ if subs. is_empty ( ) || !cmd. is_subcommand_required_set ( ) {
135+ let about = cmd
136+ . get_about ( )
137+ . map ( |a| format ! ( " — {a}" ) )
138+ . unwrap_or_default ( ) ;
139+ println_nopipe ! ( "{name}{about}" ) ;
140+ }
141+ subs. sort_by_key ( |c| c. get_name ( ) . to_owned ( ) ) ;
142+ for sub in subs {
143+ print_flat ( sub, & name) ;
144+ }
145+ }
146+
108147#[ async_trait]
109148impl RunnableCmd for CmdDocs {
110149 async fn run ( & self , _ctx : & Context ) -> Result < ( ) > {
111150 let cli = crate :: make_cli ( ) ;
112151 let mut app = cli. command_take ( ) ;
113152 app. build ( ) ;
114- let json_doc = to_json ( & app) ;
115- let pretty_json = serde_json:: to_string_pretty ( & json_doc) ?;
116- println_nopipe ! ( "{}" , pretty_json) ;
153+
154+ match self . format {
155+ DocsFormat :: Json => {
156+ let json_doc = to_json ( & app) ;
157+ let pretty_json = serde_json:: to_string_pretty ( & json_doc) ?;
158+ println_nopipe ! ( "{}" , pretty_json) ;
159+ }
160+ DocsFormat :: Text => {
161+ print_flat ( & app, "" ) ;
162+ }
163+ }
164+
117165 Ok ( ( ) )
118166 }
119167}
0 commit comments