plox/
data_source_cli_builder.rs1use std::{
4 num::{ParseFloatError, ParseIntError},
5 str::ParseBoolError,
6};
7
8use crate::graph_config::*;
9use clap::{Arg, ArgAction, Command, CommandFactory, Parser};
10
11#[derive(Debug, thiserror::Error)]
12pub enum Error {
13 #[error("CLI parsing error: {0}")]
14 GeneralCliParseError(String),
15 #[error("Parse int error: {0}")]
16 ParseIntError(#[from] ParseIntError),
17 #[error("Parse int error: {0}")]
18 ParseBoolError(#[from] ParseBoolError),
19 #[error("Parse float error: {0}")]
20 ParseFloatError(#[from] ParseFloatError),
21}
22
23impl DataSource {
24 const CLI_NAME_PLOT_FIELD: &str = "plot";
26 const CLI_NAME_EVENT: &str = "event";
27 const CLI_NAME_EVENT_COUNT: &str = "event-count";
28 const CLI_NAME_EVENT_DELTA: &str = "event-delta";
29
30 pub fn get_cli_ids() -> Vec<String> {
31 DummyDataSourceSubcommand::command()
32 .get_subcommands()
33 .map(|sc| sc.get_name().to_string().clone())
34 .collect()
35 }
36}
37
38impl DataSource {
39 pub fn try_from_flag(id: &str, val: &[&String]) -> Result<Self, Error> {
47 Ok(match id {
48 Self::CLI_NAME_EVENT => match val.len() {
49 2 => DataSource::EventValue {
50 guard: None,
51 pattern: val[0].to_string(),
52 yvalue: val[1].parse::<f64>()?,
53 },
54 3 => DataSource::EventValue {
55 guard: Some(val[0].to_string()),
56 pattern: val[1].to_string(),
57 yvalue: val[2].parse::<f64>()?,
58 },
59 _ => {
60 return Err(Error::GeneralCliParseError(format!(
61 "Bad parameter count ({}) for {}. This is bug.",
62 val.len(),
63 id
64 )));
65 },
66 },
67 Self::CLI_NAME_PLOT_FIELD => match val.len() {
80 1 => DataSource::FieldValue(FieldCaptureSpec {
81 guard: None,
82 field: val[0].to_string(),
83 }),
84 2 => DataSource::FieldValue(FieldCaptureSpec {
85 guard: Some(val[0].to_string()),
86 field: val[1].to_string(),
87 }),
88 _ => {
89 return Err(Error::GeneralCliParseError(format!(
90 "Bad parameter count ({}) for {}. This is bug.",
91 val.len(),
92 id
93 )));
94 },
95 },
96 Self::CLI_NAME_EVENT_COUNT => match val.len() {
97 1 => DataSource::EventCount { guard: None, pattern: val[0].to_string() },
98 2 => DataSource::EventCount {
99 guard: Some(val[0].to_string()),
100 pattern: val[1].to_string(),
101 },
102 _ => {
103 return Err(Error::GeneralCliParseError(format!(
104 "Bad parameter count ({}) for {}. This is bug.",
105 val.len(),
106 id
107 )));
108 },
109 },
110 Self::CLI_NAME_EVENT_DELTA => match val.len() {
111 1 => DataSource::EventDelta(EventDeltaSpec {
112 guard: None,
113 pattern: val[0].to_string(),
114 }),
115 2 => DataSource::EventDelta(EventDeltaSpec {
116 guard: Some(val[0].to_string()),
117 pattern: val[1].to_string(),
118 }),
119 _ => {
120 return Err(Error::GeneralCliParseError(format!(
121 "Bad parameter count ({}) for {}. This is bug.",
122 val.len(),
123 id
124 )));
125 },
126 },
127 _ => {
128 return Err(Error::GeneralCliParseError(format!(
129 "Unknown DataSource id:{}. This is bug",
130 id
131 )));
132 },
133 })
134 }
135}
136
137#[derive(Parser, Debug)]
141#[command(name = "dummy")]
142pub struct DummyDataSourceSubcommand {
143 #[command(subcommand)]
144 line: DataSource,
145}
146
147fn extract_help_multiline(args: &[Arg]) -> String {
148 args.iter()
149 .filter_map(|arg| {
150 arg.get_long_help()
151 .or(arg.get_help())
152 .map(|h| format!(" <{}>: {}", arg.get_id(), h))
153 })
154 .collect::<Vec<_>>()
155 .join("\n")
156}
157
158fn extract_num_args_and_names(args: &[Arg]) -> (usize, usize, Vec<String>) {
159 let mut value_names = vec![];
160 let mut required_count = 0;
161
162 for a in args {
163 value_names.push(a.get_id().to_string());
164 if a.is_required_set() {
165 required_count += 1;
166 }
167 }
168
169 let total = args.len();
170 (required_count, total, value_names)
171}
172
173pub fn build_data_source_cli(mut base: Command) -> Command {
175 let dummy_data_source_subcommand = DummyDataSourceSubcommand::command();
176 for sub in dummy_data_source_subcommand.get_subcommands() {
177 let sub_name = sub.get_name().to_string();
178 let sub_args: Vec<Arg> = sub.get_arguments().cloned().collect();
179 let sub_help = sub.get_about().unwrap_or_default();
180 let field_help = extract_help_multiline(&sub_args);
181 let (min_args, max_args, value_names) = extract_num_args_and_names(&sub_args);
182
183 let full_help = if field_help.is_empty() {
184 sub_help.to_string()
185 } else {
186 format!("{sub_help}\n{field_help}\n")
187 };
188
189 let flag = Arg::new(sub_name.clone())
190 .long(&sub_name)
191 .num_args(min_args..=max_args)
192 .action(ArgAction::Append)
193 .value_names(&value_names)
194 .help(sub_help.to_string())
195 .long_help(full_help)
196 .next_line_help(true)
197 .help_heading("Data sources - plotted line types");
198
199 base = base.arg(flag);
200 }
201
202 base
203}