1use crate::models::{CiInfo, Docs, Pipeline, Task};
2use clap::{crate_version, Arg, ArgAction, Command};
3
4pub fn construct_cli(
6 tasks: &[Task],
7 pipelines: &Option<Vec<Pipeline>>,
8 docs: &Option<Vec<Docs>>,
9 ci: &Option<CiInfo>,
10) -> clap::Command {
11 let mut cli = cli_builder(true);
12
13 if ci.is_some() {
15 cli = cli.subcommand(Command::new("ci").about("View CI pipeline information."));
17 }
18
19 if let Some(docs) = docs {
21 let docs_subcommands = build_docs_subcommands(docs);
22 cli = cli.subcommands(vec![docs_subcommands]);
23 }
24
25 let task_subcommands = build_task_subcommands(tasks);
27 cli = cli.subcommands(vec![task_subcommands]);
28
29 if let Some(pipelines) = pipelines {
31 let pipeline_subcommands = build_pipeline_subcommands(pipelines);
32 cli = cli.subcommands(vec![pipeline_subcommands]);
33 }
34 cli
35}
36
37pub fn cli_builder(strict_subcommands: bool) -> Command {
39 Command::new("rox")
40 .about("Rox: The Robust Developer Experience CLI")
41 .next_display_order(None)
42 .version(crate_version!())
43 .arg_required_else_help(true)
44 .allow_external_subcommands(!strict_subcommands)
45 .arg(
47 Arg::new("roxfile")
48 .long("file")
49 .short('f')
50 .default_value("roxfile.yml")
51 .help("Path to a Roxfile"),
52 )
53 .subcommand(
54 Command::new("logs")
55 .about("View logs for Rox invocations.")
56 .arg(
57 Arg::new("number")
58 .help("The number of logs to view.")
59 .required(false)
60 .value_parser(clap::value_parser!(i8))
61 .default_value("1"),
62 ),
63 )
64}
65
66pub fn build_docs_subcommands(docs: &[Docs]) -> Command {
67 let subcommands: Vec<Command> = docs
68 .iter()
69 .map(|doc| Command::new(&doc.name).about(doc.description.clone().unwrap_or_default()))
70 .collect();
71
72 Command::new("docs")
73 .about("Display various kinds of documentation.")
74 .next_display_order(None)
75 .arg_required_else_help(true)
76 .subcommands(subcommands)
77}
78
79pub fn build_task_subcommands(tasks: &[Task]) -> Command {
81 let subcommands: Vec<Command> = tasks
82 .iter()
83 .filter(|target| !target.hide.unwrap_or_default())
84 .map(|task| Command::new(&task.name).about(task.description.to_owned().unwrap_or_default()))
85 .collect();
86
87 Command::new("task")
88 .about("Discrete executable tasks.")
89 .long_about("Discrete units of execution containing a single runnable command.")
90 .arg_required_else_help(true)
91 .subcommands(subcommands)
92}
93
94pub fn build_pipeline_subcommands(pipelines: &[Pipeline]) -> Command {
96 let subcommands: Vec<Command> = pipelines
97 .iter()
98 .map(|pipeline| {
99 Command::new(&pipeline.name).about(pipeline.description.clone().unwrap_or_default())
100 })
101 .collect();
102
103 Command::new("pl")
104 .about("Pipelines composed of multiple tasks.")
105 .next_display_order(None)
106 .long_about("Set(s) of task(s) composed into multiple stages.")
107 .arg_required_else_help(true)
108 .arg(
109 Arg::new("parallel")
110 .long("parallel")
111 .short('p')
112 .required(false)
113 .action(ArgAction::SetTrue)
114 .help("Run the pipeline's tasks in parallel."),
115 )
116 .subcommands(subcommands)
117}