@@ -4,10 +4,12 @@ use anyhow::Result;
44use log:: { info} ;
55use serde:: Serialize ;
66use reqvire:: error:: ReqvireError ;
7- use reqvire:: { html_export , linting, ModelManager } ;
7+ use reqvire:: { linting, ModelManager } ;
88use reqvire:: index_generator;
99use globset:: GlobSet ;
1010use reqvire:: reports;
11+ use reqvire:: diagrams;
12+ use reqvire:: export;
1113use reqvire:: change_impact;
1214use reqvire:: git_commands;
1315use reqvire:: matrix_generator;
@@ -160,22 +162,17 @@ pub struct Args {
160162 #[ clap( long, short = 'c' ) ]
161163 pub config : Option < PathBuf > ,
162164
163- /// Output LLM context document
164- /// Generates a comprehensive context document with information about Reqvire
165- /// methodology, document structure, relation types, and CLI usage to help
166- /// Large Language Models understand and work with Reqvire-based projects
167- #[ clap( long) ]
168- pub llm_context : bool ,
169-
170-
171165 /// Analise change impact and provides report
172166 #[ clap( long) ]
173167 pub change_impact : bool ,
174168
175169 /// Git commit hash to use when comparing models
176170 #[ clap( long, requires = "change_impact" , default_value = "HEAD" ) ]
177171 pub git_commit : String ,
178-
172+
173+ /// Process only files within a specific subdirectory relative to git root (hidden flag for testing)
174+ #[ clap( long, hide = true ) ]
175+ pub subdirectory : Option < String > ,
179176
180177}
181178
@@ -218,157 +215,137 @@ fn print_validation_results(errors: &[ReqvireError], json_output: bool) {
218215
219216pub fn handle_command (
220217 args : Args ,
221- specification_folder_path : & PathBuf ,
222- external_folders_path : & [ PathBuf ] ,
223218 output_folder_path : & PathBuf ,
224219 excluded_filename_patterns : & GlobSet ,
225- diagram_direction : & str
220+ diagram_direction : & str ,
221+ diagrams_with_blobs : bool ,
222+ user_requirements_root_folder : & Option < PathBuf >
226223) -> Result < i32 , ReqvireError > {
227-
224+
228225 let mut model_manager = ModelManager :: new ( ) ;
229-
230-
231- // Handle LLM context
232- if args. llm_context {
233- // Include the LLM context content directly in the binary
234- let llm_context = include_str ! ( "llm_context.md" ) ;
235- println ! ( "{}" , llm_context) ;
236- Args :: print_help ( ) ;
237- return Ok ( 0 ) ;
238- } else {
239-
240- let parse_result=model_manager. parse_and_validate ( None , & specification_folder_path, & external_folders_path, excluded_filename_patterns) ;
241-
242-
243- if args. validate {
244- match parse_result {
245- Ok ( errors) => {
246- if errors. is_empty ( ) {
247- println ! ( "✅ Validation completed successfully with no errors." ) ;
248- } else {
249- print_validation_results ( & errors, args. json ) ;
250- }
251- return Ok ( 0 ) ;
252- }
253- Err ( e) => {
254- eprintln ! ( "❌ Validation failed: {}" , e) ;
255- return Ok ( 0 ) ;
226+ let parse_result = model_manager. parse_and_validate (
227+ None ,
228+ user_requirements_root_folder,
229+ excluded_filename_patterns
230+ ) ;
231+
232+ if args. validate {
233+ match parse_result {
234+ Ok ( errors) => {
235+ if errors. is_empty ( ) {
236+ println ! ( "✅ Validation completed successfully with no errors." ) ;
237+ } else {
238+ print_validation_results ( & errors, args. json ) ;
256239 }
257- }
258- } else if args. generate_index {
259- info ! ( "Generating index....." ) ;
260-
261- let _index_context = index_generator:: generate_readme_index (
262- & model_manager. element_registry ,
263- & specification_folder_path,
264- & external_folders_path
240+ return Ok ( 0 ) ;
241+ }
242+ Err ( e) => {
243+ eprintln ! ( "❌ Validation failed: {}" , e) ;
244+ return Ok ( 0 ) ;
245+ }
246+ }
247+ } else if args. generate_index {
248+ info ! ( "Generating index....." ) ;
249+ let _index_context = index_generator:: generate_readme_index (
250+ & model_manager. element_registry ,
251+ & output_folder_path
265252 ) . map_err ( |e| {
266253 ReqvireError :: ProcessError ( format ! ( "❌ Failed to generate README.md: {:?}" , e) )
267254 } ) ?;
268255
269- return Ok ( 0 ) ;
256+ return Ok ( 0 ) ;
270257
271- } else if args. generate_diagrams {
272- info ! ( "Generating mermaid diagrams in {:?}" , specification_folder_path ) ;
273- // Only collect identifiers and process files to add diagrams
274- // Skip validation checks for diagram generation mode
275- model_manager . process_diagrams ( & specification_folder_path , & external_folders_path , diagram_direction) ?;
258+ } else if args. generate_diagrams {
259+ info ! ( "Generating mermaid diagrams" ) ;
260+ // Only collect identifiers and process files to add diagrams
261+ // Skip validation checks for diagram generation mode
262+ diagrams :: process_diagrams ( & model_manager . element_registry , diagram_direction, diagrams_with_blobs ) ?;
276263
277- info ! ( "Requirements diagrams updated in source files" ) ;
278- return Ok ( 0 ) ;
279-
280- } else if args. model_summary {
281- let filters = Filters :: new (
282- args. filter_file . as_deref ( ) ,
283- args. filter_name . as_deref ( ) ,
284- args. filter_section . as_deref ( ) ,
285- args. filter_type . as_deref ( ) ,
286- args. filter_content . as_deref ( ) ,
287- args. filter_is_not_verified ,
288- args. filter_is_not_satisfied ,
289- ) . map_err ( |e| {
290- ReqvireError :: ProcessError ( format ! ( "❌ Failed to construct filters: {}" , e) )
291- } ) ?;
292-
293- let output_format = if args. cypher {
294- reports:: SummaryOutputFormat :: Cypher
295- } else if args. json {
296- reports:: SummaryOutputFormat :: Json
297- } else {
298- reports:: SummaryOutputFormat :: Text
299- } ;
300-
301-
302- reports:: print_registry_summary ( & model_manager. element_registry , output_format, & filters) ;
303- return Ok ( 0 ) ;
304-
305-
306- } else if args. change_impact {
307-
308- let current_commit = git_commands:: get_commit_hash ( ) . map_err ( |_| {
309- ReqvireError :: ProcessError ( "❌ Failed to retrieve the current commit hash." . to_string ( ) )
310- } ) ?;
311-
312- let repo_root = git_commands:: repository_root ( ) . map_err ( |_| {
313- ReqvireError :: ProcessError ( "❌ Failed to determine repository root." . to_string ( ) )
314- } ) ?;
315-
316- let base_url = git_commands:: get_repository_base_url ( ) . map_err ( |_| {
317- ReqvireError :: ProcessError ( "❌ Failed to determine repository base url." . to_string ( ) )
318- } ) ?;
319-
264+ info ! ( "Requirements diagrams updated in source files" ) ;
265+ return Ok ( 0 ) ;
320266
321- let mut refference_model_manager = ModelManager :: new ( ) ;
322- let _not_interested=refference_model_manager. parse_and_validate ( Some ( & args. git_commit ) , & specification_folder_path, & external_folders_path, excluded_filename_patterns) ;
323-
324- let report=change_impact:: compute_change_impact (
325- & model_manager. element_registry ,
326- & refference_model_manager. element_registry ,
327- & repo_root,
328- & specification_folder_path,
329- & external_folders_path
330- )
331- . map_err ( |e| ReqvireError :: ProcessError ( format ! ( "❌ Failed to generate change impact report: {:?}" , e) ) ) ?;
267+ } else if args. model_summary {
268+ let filters = Filters :: new (
269+ args. filter_file . as_deref ( ) ,
270+ args. filter_name . as_deref ( ) ,
271+ args. filter_section . as_deref ( ) ,
272+ args. filter_type . as_deref ( ) ,
273+ args. filter_content . as_deref ( ) ,
274+ args. filter_is_not_verified ,
275+ args. filter_is_not_satisfied ,
276+ ) . map_err ( |e| {
277+ ReqvireError :: ProcessError ( format ! ( "❌ Failed to construct filters: {}" , e) )
278+ } ) ?;
279+
280+ let output_format = if args. cypher {
281+ reports:: SummaryOutputFormat :: Cypher
282+ } else if args. json {
283+ reports:: SummaryOutputFormat :: Json
284+ } else {
285+ reports:: SummaryOutputFormat :: Text
286+ } ;
287+
288+ reports:: print_registry_summary ( & model_manager. element_registry , output_format, & filters) ;
289+ return Ok ( 0 ) ;
290+
332291
333- report. print ( & base_url, & current_commit, & args. git_commit , args. json ) ;
334-
335- return Ok ( 0 ) ;
292+ } else if args. change_impact {
293+
294+ let base_url = git_commands:: get_repository_base_url ( ) . map_err ( |_| {
295+ ReqvireError :: ProcessError ( "❌ Failed to determine repository base url." . to_string ( ) )
296+ } ) ?;
297+
298+ let current_commit = git_commands:: get_commit_hash ( ) . map_err ( |_| {
299+ ReqvireError :: ProcessError ( "❌ Failed to retrieve the current commit hash." . to_string ( ) )
300+ } ) ?;
301+
302+ let mut refference_model_manager = ModelManager :: new ( ) ;
303+ let _not_interested=refference_model_manager. parse_and_validate ( Some ( & args. git_commit ) , user_requirements_root_folder, excluded_filename_patterns) ;
304+
305+ let report=change_impact:: compute_change_impact (
306+ & model_manager. element_registry ,
307+ & refference_model_manager. element_registry
308+ )
309+ . map_err ( |e| ReqvireError :: ProcessError ( format ! ( "❌ Failed to generate change impact report: {:?}" , e) ) ) ?;
310+
311+ report. print ( & base_url, & current_commit, & args. git_commit , args. json ) ;
336312
313+ return Ok ( 0 ) ;
314+
337315
338- } else if args. lint {
339- linting:: run_linting ( & specification_folder_path, & external_folders_path, excluded_filename_patterns, args. dry_run ) ?;
340- return Ok ( 0 ) ;
341-
342-
343- } else if args. traces {
344- let matrix_config = matrix_generator:: MatrixConfig :: default ( ) ;
316+ } else if args. lint {
317+ linting:: run_linting ( excluded_filename_patterns, args. dry_run , args. subdirectory . as_deref ( ) ) ?;
318+ return Ok ( 0 ) ;
319+
320+ } else if args. traces {
321+ let matrix_config = matrix_generator:: MatrixConfig :: default ( ) ;
345322
346- let matrix_output = reqvire:: matrix_generator:: generate_matrix (
347- & model_manager. element_registry ,
348- & matrix_config,
349- if args. json {
350- matrix_generator:: MatrixFormat :: Json
351- } else if args. svg {
352- matrix_generator:: MatrixFormat :: Svg
353- } else {
354- matrix_generator:: MatrixFormat :: Markdown
355- } ,
356- ) ;
323+ let matrix_output = reqvire:: matrix_generator:: generate_matrix (
324+ & model_manager. element_registry ,
325+ & matrix_config,
326+ if args. json {
327+ matrix_generator:: MatrixFormat :: Json
328+ } else if args. svg {
329+ matrix_generator:: MatrixFormat :: Svg
330+ } else {
331+ matrix_generator:: MatrixFormat :: Markdown
332+ } ,
333+ ) ;
357334
358- println ! ( "{}" , matrix_output) ;
359- return Ok ( 0 ) ;
335+ println ! ( "{}" , matrix_output) ;
336+ return Ok ( 0 ) ;
360337
361338
362- } else if args. html {
363- let processed_count = html_export:: export_markdown_to_html ( specification_folder_path, & external_folders_path, output_folder_path) ?;
364- info ! ( "{} markdown files converted to HTML" , processed_count) ;
365- return Ok ( 0 ) ;
366- } else {
367- Args :: print_help ( ) ;
339+ } else if args. html {
340+ let processed_count = export:: export_model ( & model_manager. element_registry , output_folder_path) ?;
341+ info ! ( "{} markdown files converted to HTML" , processed_count) ;
342+
343+ return Ok ( 0 ) ;
344+ } else {
345+ Args :: print_help ( ) ;
368346
369- }
370-
371347 }
348+
372349 Ok ( 1 )
373350}
374351
@@ -402,7 +379,6 @@ mod tests {
402379 fn test_handle_command ( ) {
403380 // Mock CLI arguments
404381 let args = Args {
405- llm_context : false ,
406382 html : false ,
407383 lint : false ,
408384 dry_run : false ,
@@ -423,7 +399,8 @@ mod tests {
423399 validate : false ,
424400 config : None , // No custom config file for the test
425401 change_impact : false , // Add the missing field
426- git_commit : "HEAD" . to_string ( ) , // Add the missing field with default value
402+ git_commit : "HEAD" . to_string ( ) , // Add the missing field
403+ subdirectory : None
427404 } ;
428405
429406
@@ -444,13 +421,14 @@ mod tests {
444421
445422
446423 // Run the handle_command function
424+ let user_requirements_root = None ;
447425 let result = handle_command (
448426 args,
449- & specification_folder_path,
450- & external_folders_path,
451427 & output_folder_path,
452428 & build_glob_set ( & excluded_filename_patterns) ,
453429 "TD" ,
430+ false ,
431+ & user_requirements_root
454432 ) ;
455433
456434 // Assert that it runs without error
0 commit comments