1use clap::{Arg, ArgAction, Command};
2use std::env;
3
4#[derive(Debug)]
5pub enum Error {
6 #[allow(dead_code)]
7 ModuleNotFound(String),
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum PrintOutput {
12 Yaml,
13 Json,
14}
15
16#[derive(Debug)]
17pub struct Cli {
18 pub main_target: Option<String>,
19 pub only_download_modules: bool,
20 pub package_path: Option<String>,
21 pub package_target: String,
22 pub create_tar: bool,
23 pub no_run: bool,
24 pub download: bool,
25 pub print_yaml: bool,
26 pub print_output: PrintOutput,
27 pub test: bool,
28 pub test_filter: Option<String>,
29 pub var_main: Option<String>,
30 pub var_payload: Option<String>,
31 pub start_step: Option<String>,
32 pub analyzer: bool,
34 pub analyzer_files: bool,
35 pub analyzer_modules: bool,
36 pub analyzer_total_steps: bool,
37 pub analyzer_total_pipelines: bool,
38 pub analyzer_json: bool,
39 pub analyzer_inner: bool,
40 pub analyzer_all: bool,
41}
42
43impl Cli {
44 pub fn load() -> Result<Cli, Error> {
45 let command = Command::new("Phlow Runtime")
46 .version(env!("CARGO_PKG_VERSION"))
47 .arg(
48 Arg::new("main_path")
49 .help("Main path/file to load")
50 .required(false)
51 .index(1)
52 .num_args(1..), )
54 .arg(
55 Arg::new("install")
56 .long("install")
57 .short('i')
58 .action(ArgAction::SetTrue)
59 .value_parser(clap::builder::BoolishValueParser::new())
60 .help("Install dependencies")
61 .default_value("false"),
62 )
63 .arg(
64 Arg::new("download")
65 .long("download")
66 .short('d')
67 .help("Enable download modules before running")
68 .value_parser(clap::builder::BoolValueParser::new())
69 .default_value("true"),
70 )
71 .arg(
72 Arg::new("package")
73 .long("package")
74 .help("Path to the package file"),
75 )
76 .arg(
77 Arg::new("package_target")
78 .long("package-target")
79 .help("Target directory for package output")
80 .default_value("./phlow_packages"),
81 )
82 .arg(
83 Arg::new("tar")
84 .long("tar")
85 .help("Create a .tar.gz archive instead of extracting to phlow_packages/")
86 .value_parser(clap::builder::BoolishValueParser::new())
87 .action(ArgAction::SetTrue)
88 .default_value("false"),
89 )
90 .arg(
91 Arg::new("no_run")
92 .long("no-run")
93 .short('n')
94 .help("Do not run the main file")
95 .value_parser(clap::builder::BoolishValueParser::new())
96 .action(ArgAction::SetTrue)
97 .default_value("false"),
98 )
99 .arg(
100 Arg::new("print_yaml")
101 .long("print")
102 .short('p')
103 .help("Print the YAML file generated from the target file")
104 .value_parser(clap::builder::BoolishValueParser::new())
105 .action(ArgAction::SetTrue)
106 .default_value("false"),
107 )
108 .arg(
109 Arg::new("print_output")
110 .long("output")
111 .short('o')
112 .help("Output format for --print (yaml|json)")
113 .value_name("FORMAT")
114 .value_parser(["yaml", "json"])
115 .requires("print_yaml"),
116 )
117 .arg(
118 Arg::new("test")
119 .long("test")
120 .short('t')
121 .help("Run tests defined in the phlow file")
122 .value_parser(clap::builder::BoolishValueParser::new())
123 .action(ArgAction::SetTrue)
124 .default_value("false"),
125 )
126 .arg(
127 Arg::new("test_filter")
128 .long("test-filter")
129 .help("Filter tests by description (substring match)")
130 .requires("test")
131 .value_name("FILTER"),
132 )
133 .arg(
134 Arg::new("var_main")
135 .long("var-main")
136 .help("Set the main variable value")
137 .value_name("VALUE"),
138 )
139 .arg(
140 Arg::new("var_payload")
141 .long("var-payload")
142 .help("Set the initial payload value")
143 .value_name("VALUE"),
144 )
145 .arg(
146 Arg::new("var_pre")
147 .long("var-pre")
148 .help("Set the preprocessor variable value")
149 .value_name("PRE_VALUE"),
150 )
151 .arg(
152 Arg::new("start_step")
153 .long("step")
154 .help("Start execution from the step id")
155 .value_name("STEP_ID"),
156 );
157 let command = command
159 .arg(
160 Arg::new("analyzer")
161 .long("analyzer")
162 .help("Run analyzer on the .phlow and return info without executing the runtime")
163 .action(ArgAction::SetTrue)
164 .value_parser(clap::builder::BoolishValueParser::new())
165 .default_value("false"),
166 )
167 .arg(
168 Arg::new("files")
169 .long("files")
170 .help("Return all files loaded by the project, including files referenced by !include or !import")
171 .action(ArgAction::SetTrue),
172 )
173 .arg(
174 Arg::new("modules")
175 .long("modules")
176 .help("Return modules declared in the project and whether they are downloaded in phlow_packages")
177 .action(ArgAction::SetTrue),
178 )
179 .arg(
180 Arg::new("total_steps")
181 .long("total-steps")
182 .help("Return total steps in the project")
183 .action(ArgAction::SetTrue),
184 )
185 .arg(
186 Arg::new("total_pipelines")
187 .long("total-pipelines")
188 .help("Return total pipelines in the project")
189 .action(ArgAction::SetTrue),
190 )
191 .arg(
192 Arg::new("json")
193 .long("json")
194 .help("Output analyzer data as JSON")
195 .action(ArgAction::SetTrue),
196 )
197 .arg(
198 Arg::new("text")
199 .long("text")
200 .help("Output analyzer data as plain text (default)")
201 .action(ArgAction::SetTrue),
202 );
203
204 let command = command.arg(
206 Arg::new("inner")
207 .long("inner")
208 .help("When used with analyzer, allow analysis/recursive loading of internal modules (those declared with a leading '.')")
209 .action(ArgAction::SetTrue),
210 );
211
212 let command = command.arg(
214 Arg::new("all")
215 .long("all")
216 .help("When used with --analyzer, return all available analyzer information")
217 .action(ArgAction::SetTrue),
218 );
219
220 let matches = command.get_matches();
221
222 let main = match matches.get_one::<String>("main_path") {
223 Some(target) => Some(target.clone()),
224 None => None,
225 };
226
227 let install = *matches.get_one::<bool>("install").unwrap_or(&false);
228 let package_path = matches.get_one::<String>("package").map(|s| s.to_string());
229 let package_target = matches
230 .get_one::<String>("package_target")
231 .map(|s| s.to_string())
232 .unwrap_or_else(|| "./phlow_packages".to_string());
233 let create_tar = *matches.get_one::<bool>("tar").unwrap_or(&false);
234
235 let no_run = *matches.get_one::<bool>("no_run").unwrap_or(&false);
236
237 let download = *matches.get_one::<bool>("download").unwrap_or(&true);
238
239 let print_yaml = *matches.get_one::<bool>("print_yaml").unwrap_or(&false);
240 let print_output = matches
241 .get_one::<String>("print_output")
242 .map(|s| match s.as_str() {
243 "json" => PrintOutput::Json,
244 _ => PrintOutput::Yaml,
245 })
246 .unwrap_or(PrintOutput::Yaml);
247
248 let test = *matches.get_one::<bool>("test").unwrap_or(&false);
249
250 let test_filter = matches
251 .get_one::<String>("test_filter")
252 .map(|s| s.to_string());
253
254 let var_main = matches.get_one::<String>("var_main").map(|s| s.to_string());
255 let var_payload = matches
256 .get_one::<String>("var_payload")
257 .map(|s| s.to_string());
258 let start_step = matches
259 .get_one::<String>("start_step")
260 .map(|s| s.to_string());
261
262 let analyzer = *matches.get_one::<bool>("analyzer").unwrap_or(&false);
263 let analyzer_files = *matches.get_one::<bool>("files").unwrap_or(&false);
264 let analyzer_modules = *matches.get_one::<bool>("modules").unwrap_or(&false);
265 let analyzer_total_steps = *matches.get_one::<bool>("total_steps").unwrap_or(&false);
266 let analyzer_total_pipelines =
267 *matches.get_one::<bool>("total_pipelines").unwrap_or(&false);
268 let analyzer_json = *matches.get_one::<bool>("json").unwrap_or(&false);
269 let analyzer_inner = *matches.get_one::<bool>("inner").unwrap_or(&false);
270 let analyzer_all = *matches.get_one::<bool>("all").unwrap_or(&false);
271 Ok(Cli {
272 main_target: main,
273 only_download_modules: install,
274 package_path,
275 package_target,
276 create_tar,
277 no_run,
278 download,
279 print_yaml,
280 print_output,
281 test,
282 test_filter,
283 var_main,
284 var_payload,
285 start_step,
286 analyzer,
287 analyzer_files,
288 analyzer_modules,
289 analyzer_total_steps,
290 analyzer_total_pipelines,
291 analyzer_json,
292 analyzer_inner,
293 analyzer_all,
294 })
295 }
296}