blvm_sdk/module/
cli_args.rs1use blvm_node::module::ipc::protocol::CliArgSpec;
6use blvm_node::module::traits::ModuleError;
7use std::collections::HashMap;
8
9pub fn parse_args(
16 args: &[String],
17 arg_specs: &[CliArgSpec],
18) -> Result<HashMap<String, String>, ModuleError> {
19 let mut map = HashMap::new();
20
21 let has_named = args.iter().any(|a| {
23 a.starts_with("--")
24 || (a.starts_with('-')
25 && a.len() > 1
26 && !a
27 .chars()
28 .nth(1)
29 .map(|c| c.is_ascii_digit())
30 .unwrap_or(false))
31 });
32
33 if has_named {
34 let mut i = 0;
36 while i < args.len() {
37 let arg = &args[i];
38 if arg.starts_with("--") {
39 let key = arg.trim_start_matches('-');
40 if key.is_empty() {
41 i += 1;
42 continue;
43 }
44 let param_name = find_param_by_long(arg_specs, key);
45 i += 1;
46 if i < args.len() && !args[i].starts_with('-') {
47 let value = args[i].clone();
48 i += 1;
49 map.insert(param_name.to_string(), value);
50 } else {
51 map.insert(param_name.to_string(), "true".to_string());
53 }
54 } else if arg.starts_with('-') && arg.len() > 1 {
55 let short = &arg[1..];
56 let param_name = find_param_by_short(arg_specs, short);
57 i += 1;
58 if i < args.len() && !args[i].starts_with('-') {
59 let value = args[i].clone();
60 i += 1;
61 map.insert(param_name.to_string(), value);
62 } else {
63 map.insert(param_name.to_string(), "true".to_string());
64 }
65 } else {
66 i += 1;
67 }
68 }
69 } else {
70 for (i, spec) in arg_specs.iter().enumerate() {
72 if let Some(v) = args.get(i) {
73 map.insert(spec.name.clone(), v.clone());
74 }
75 }
76 }
77
78 Ok(map)
79}
80
81fn find_param_by_long<'a>(specs: &'a [CliArgSpec], long: &'a str) -> &'a str {
82 for spec in specs {
83 let long_form = spec.long_name.as_deref().unwrap_or(&spec.name);
84 if long_form == long {
85 return &spec.name;
86 }
87 }
88 long
89}
90
91fn find_param_by_short<'a>(specs: &'a [CliArgSpec], short: &'a str) -> &'a str {
92 for spec in specs {
93 if let Some(s) = &spec.short_name {
94 if s == short {
95 return &spec.name;
96 }
97 }
98 if let Some(c) = short.chars().next() {
99 if spec.name.starts_with(c) {
100 return &spec.name;
101 }
102 }
103 }
104 short
105}
106
107pub fn coerce_bool(s: &str) -> Result<bool, ModuleError> {
111 let s = s.to_lowercase();
112 if s == "true" || s == "1" || s == "yes" || s == "on" {
113 Ok(true)
114 } else if s == "false" || s == "0" || s == "no" || s == "off" {
115 Ok(false)
116 } else {
117 Err(ModuleError::Other(format!("invalid bool: {s}")))
118 }
119}