@@ -7,7 +7,15 @@ use std::path::PathBuf;
77#[ derive( Parser ) ]
88#[ command( name = "plotnik" , bin_name = "plotnik" ) ]
99#[ command( about = "Query language for tree-sitter AST with type inference" ) ]
10- #[ command( after_help = r#"OUTPUT DEPENDENCIES:
10+ struct Cli {
11+ #[ command( subcommand) ]
12+ command : Command ,
13+ }
14+
15+ #[ derive( Subcommand ) ]
16+ enum Command {
17+ /// Debug and inspect queries and source files
18+ #[ command( after_help = r#"OUTPUT DEPENDENCIES:
1119┌─────────────────┬─────────────┬──────────────┐
1220│ Output │ Needs Query │ Needs Source │
1321├─────────────────┼─────────────┼──────────────┤
@@ -22,40 +30,32 @@ use std::path::PathBuf;
2230
2331EXAMPLES:
2432 # Parse and typecheck query only
25- plotnik --query-text '(identifier) @id' --query-cst --query-types
33+ plotnik debug --query-text '(identifier) @id' --query-cst --query-types
2634
2735 # Dump tree-sitter AST of source file
28- plotnik --source-file app.ts --source-ast
36+ plotnik debug --source-file app.ts --source-ast
2937
3038 # Full pipeline: match query against source
31- plotnik --query-file rules.pql --source-file app.ts --result
39+ plotnik debug --query-file rules.pql --source-file app.ts --result
3240
3341 # Debug with trace
34- plotnik --query-text '(function_declaration) @fn' \
35- --source-text 'function foo() {}' --lang typescript --trace
42+ plotnik debug --query-text '(function_declaration) @fn' \
43+ --source-text 'function foo() {}' --lang typescript --trace"# ) ]
44+ Debug {
45+ #[ command( flatten) ]
46+ query : QueryArgs ,
3647
37- # Show documentation
38- plotnik docs reference"# ) ]
39- struct Cli {
40- #[ command( subcommand) ]
41- command : Option < Command > ,
42-
43- #[ command( flatten) ]
44- query : QueryArgs ,
48+ #[ command( flatten) ]
49+ source : SourceArgs ,
4550
46- #[ command( flatten) ]
47- source : SourceArgs ,
48-
49- /// Language for source (required for --source-text, inferred from extension for --source-file)
50- #[ arg( long, short = 'l' , value_name = "LANG" ) ]
51- lang : Option < String > ,
51+ /// Language for source (required for --source-text, inferred from extension for --source-file)
52+ #[ arg( long, short = 'l' , value_name = "LANG" ) ]
53+ lang : Option < String > ,
5254
53- #[ command( flatten) ]
54- output : OutputArgs ,
55- }
55+ #[ command( flatten) ]
56+ output : OutputArgs ,
57+ } ,
5658
57- #[ derive( Subcommand ) ]
58- enum Command {
5959 /// Print documentation
6060 Docs {
6161 /// Topic to display (e.g., "reference", "examples")
@@ -121,19 +121,32 @@ struct OutputArgs {
121121fn main ( ) {
122122 let cli = Cli :: parse ( ) ;
123123
124- if let Some ( Command :: Docs { topic } ) = cli. command {
125- print_help ( topic. as_deref ( ) ) ;
126- return ;
124+ match cli. command {
125+ Command :: Docs { topic } => {
126+ print_help ( topic. as_deref ( ) ) ;
127+ }
128+ Command :: Debug {
129+ query,
130+ source,
131+ lang,
132+ output,
133+ } => {
134+ run_debug ( query, source, lang, output) ;
135+ }
127136 }
137+ }
128138
129- let has_query = cli. query . query_text . is_some ( ) || cli. query . query_file . is_some ( ) ;
130- let has_source = cli. source . source_text . is_some ( ) || cli. source . source_file . is_some ( ) ;
139+ fn run_debug (
140+ query_args : QueryArgs ,
141+ source_args : SourceArgs ,
142+ lang : Option < String > ,
143+ output : OutputArgs ,
144+ ) {
145+ let has_query = query_args. query_text . is_some ( ) || query_args. query_file . is_some ( ) ;
146+ let has_source = source_args. source_text . is_some ( ) || source_args. source_file . is_some ( ) ;
131147
132148 // Validate output dependencies
133- if ( cli. output . query_cst
134- || cli. output . query_ast
135- || cli. output . query_refs
136- || cli. output . query_types )
149+ if ( output. query_cst || output. query_ast || output. query_refs || output. query_types )
137150 && !has_query
138151 {
139152 eprintln ! (
@@ -142,81 +155,81 @@ fn main() {
142155 std:: process:: exit ( 1 ) ;
143156 }
144157
145- if cli . output . source_ast && !has_source {
158+ if output. source_ast && !has_source {
146159 eprintln ! ( "error: --source-ast requires --source-text or --source-file" ) ;
147160 std:: process:: exit ( 1 ) ;
148161 }
149162
150- if cli . output . trace && !( has_query && has_source) {
163+ if output. trace && !( has_query && has_source) {
151164 eprintln ! ( "error: --trace requires both query and source inputs" ) ;
152165 std:: process:: exit ( 1 ) ;
153166 }
154167
155- if cli . output . result && !( has_query && has_source) {
168+ if output. result && !( has_query && has_source) {
156169 eprintln ! ( "error: --result requires both query and source inputs" ) ;
157170 std:: process:: exit ( 1 ) ;
158171 }
159172
160173 // If both inputs provided and no outputs selected, default to --result
161- let show_result = cli . output . result
174+ let show_result = output. result
162175 || ( has_query
163176 && has_source
164- && !cli . output . query_cst
165- && !cli . output . query_ast
166- && !cli . output . query_refs
167- && !cli . output . query_types
168- && !cli . output . source_ast
169- && !cli . output . trace ) ;
177+ && !output. query_cst
178+ && !output. query_ast
179+ && !output. query_refs
180+ && !output. query_types
181+ && !output. source_ast
182+ && !output. trace ) ;
170183
171184 // Validate --lang requirement
172- if cli . source . source_text . is_some ( ) && cli . lang . is_none ( ) {
185+ if source_args . source_text . is_some ( ) && lang. is_none ( ) {
173186 eprintln ! ( "error: --lang is required when using --source-text" ) ;
174187 std:: process:: exit ( 1 ) ;
175188 }
176189
177190 // Load query if needed
178191 let query_source = if has_query {
179- Some ( load_query ( & cli . query ) )
192+ Some ( load_query ( & query_args ) )
180193 } else {
181194 None
182195 } ;
183196
184197 let query = query_source. as_ref ( ) . map ( |src| Query :: new ( src) ) ;
185198
186- if cli . output . query_cst {
199+ if output. query_cst {
187200 println ! ( "=== QUERY CST ===" ) ;
188201 if let Some ( ref q) = query {
189202 print ! ( "{}" , q. format_cst( ) ) ;
190203 }
191204 }
192205
193- if cli . output . query_ast {
206+ if output. query_ast {
194207 println ! ( "=== QUERY AST ===" ) ;
195208 if let Some ( ref q) = query {
196209 print ! ( "{}" , q. format_ast( ) ) ;
197210 }
198211 }
199212
200- if cli . output . query_refs {
213+ if output. query_refs {
201214 println ! ( "=== QUERY REFS ===" ) ;
202215 if let Some ( ref q) = query {
203216 print ! ( "{}" , q. format_refs( ) ) ;
204217 }
205218 }
206219
207- if cli . output . query_types {
220+ if output. query_types {
208221 println ! ( "=== QUERY TYPES ===" ) ;
209222 println ! ( "(not implemented)" ) ;
210223 println ! ( ) ;
211224 }
212225
213- if cli . output . source_ast {
226+ if output. source_ast {
214227 println ! ( "=== SOURCE AST ===" ) ;
215228 println ! ( "(not implemented)" ) ;
216229 println ! ( ) ;
217230 }
218231
219- if cli . output . trace {
232+ if output. trace {
220233 println ! ( "=== TRACE ===" ) ;
221234 println ! ( "(not implemented)" ) ;
222235 println ! ( ) ;
@@ -263,15 +276,14 @@ fn print_help(topic: Option<&str>) {
263276 println ! ( "Usage: plotnik docs <topic>" ) ;
264277 }
265278 Some ( "reference" ) => {
266- // TODO: include_str! the actual REFERENCE.md
267279 println ! ( "{}" , include_str!( "../../../docs/REFERENCE.md" ) ) ;
268280 }
269281 Some ( "examples" ) => {
270282 println ! ( "(examples not yet written)" ) ;
271283 }
272284 Some ( other) => {
273285 eprintln ! ( "Unknown help topic: {}" , other) ;
274- eprintln ! ( "Run 'plotnik help ' to see available topics" ) ;
286+ eprintln ! ( "Run 'plotnik docs ' to see available topics" ) ;
275287 std:: process:: exit ( 1 ) ;
276288 }
277289 }
0 commit comments