bpf_loader_lib/meta/
arg_builder.rs1use anyhow::{bail, Result};
8use clap::{Arg, ArgAction, Command};
9use serde_json::Value;
10
11use super::EunomiaObjectMeta;
12
13const DEFAULT_DESCRIPTION: &str = "A simple eBPF program";
14const DEFAULT_VERSION: &str = "0.1.0";
15const DEFAULT_EPILOG: &str = "Built with eunomia-bpf framework.\nSee https://github.com/eunomia-bpf/eunomia-bpf for more information.";
16
17impl EunomiaObjectMeta {
18 pub fn build_argument_parser(&self) -> Result<Command> {
33 let cmd = Command::new(self.bpf_skel.obj_name.to_string());
34
35 let cmd = if let Some(doc) = &self.bpf_skel.doc {
36 cmd.version(
37 doc.version
38 .to_owned()
39 .unwrap_or_else(|| DEFAULT_VERSION.to_string()),
40 )
41 .after_help(
42 doc.details
43 .to_owned()
44 .unwrap_or_else(|| DEFAULT_EPILOG.to_owned()),
45 )
46 .before_help(
47 doc.brief
48 .to_owned()
49 .or(doc.description.to_owned())
50 .unwrap_or_else(|| DEFAULT_DESCRIPTION.to_owned()),
51 )
52 } else {
53 cmd.version(DEFAULT_VERSION)
54 .after_help(DEFAULT_EPILOG)
55 .before_help(DEFAULT_DESCRIPTION)
56 };
57 let mut cmd = cmd.arg(
59 Arg::new("verbose")
60 .long("verbose")
61 .action(ArgAction::SetTrue)
62 .help("Whether to show libbpf debug information"),
63 );
64 for section in self.bpf_skel.data_sections.iter() {
66 for variable in section.variables.iter() {
67 if variable.name.starts_with("__eunomia_dummy") {
69 continue;
70 }
71 let help = variable
72 .cmdarg
73 .help
74 .to_owned()
75 .or(variable.description.to_owned())
76 .unwrap_or_else(|| {
77 format!("Set value of `{}` variable {}", variable.ty, variable.name)
78 });
79
80 let long = variable
81 .cmdarg
82 .long
83 .to_owned()
84 .unwrap_or_else(|| variable.name.to_string());
85 if variable.ty == "bool" {
86 let default = if let Some(val) = variable
88 .cmdarg
89 .default
90 .to_owned()
91 .or(variable.value.to_owned())
92 {
93 Some(match val {
94 Value::Bool(b) => b,
95 _ => bail!("Only expected bool values in bool variables"),
96 })
97 } else {
98 None
99 };
100 let arg = match default {
101 None => Arg::new(variable.name.clone())
103 .help(help)
104 .long(long)
105 .action(ArgAction::SetTrue),
106 Some(true) => Arg::new(variable.name.clone())
107 .help(help)
108 .long(format!("disable-{long}"))
109 .default_value("true")
110 .action(ArgAction::SetFalse),
111 Some(false) => Arg::new(variable.name.clone())
112 .help(help)
113 .long(format!("enable-{long}"))
114 .action(ArgAction::SetTrue),
115 };
116 cmd = cmd.arg(arg);
117 } else {
118 let short = variable.cmdarg.short.to_owned();
119
120 let default = if let Some(default) = variable
121 .cmdarg
122 .default
123 .to_owned()
124 .or(variable.value.to_owned())
125 {
126 Some(match default {
127 Value::Number(v) => v.to_string(),
128 Value::String(v) => v,
129 _ => bail!(
130 "We only want to see integers or strings in default values for non-bool variables.."
131 ),
132 })
133 } else {
134 None
135 };
136 let arg = Arg::new(variable.name.clone())
137 .action(ArgAction::Set)
138 .help(help)
139 .long(long);
140 let arg = if let Some(s) = short {
141 let chars = s.chars().collect::<Vec<char>>();
142 if chars.len() != 1 {
143 bail!(
144 "Short name for variable `{}` is expected to be just in 1 character",
145 variable.name
146 );
147 }
148
149 arg.short(chars[0])
150 } else {
151 arg
152 };
153 let arg = if let Some(default) = default {
156 arg.default_value(default)
157 } else {
158 arg
159 };
160 cmd = cmd.arg(arg);
161 }
162 }
163 }
164 Ok(cmd)
165 }
166}
167#[cfg(test)]
168mod tests {
169 use crate::{meta::EunomiaObjectMeta, tests::get_assets_dir};
170
171 #[test]
172 fn test_arg_builder() {
173 let skel = serde_json::from_str::<EunomiaObjectMeta>(
174 &std::fs::read_to_string(get_assets_dir().join("arg_builder_test").join("skel.json"))
175 .unwrap(),
176 )
177 .unwrap();
178 let cmd = skel.build_argument_parser().unwrap();
179 for p in cmd.get_arguments() {
180 println!("{:?}", p.get_long());
181 }
182 let cmd = cmd.color(clap::ColorChoice::Never);
183 let matches = cmd
184 .try_get_matches_from([
185 "myprog",
186 "--cv1",
187 "2333",
188 "--const_val_2",
189 "12345678",
190 "--const_val_3",
191 "abcdefg",
192 "--bss_val_1",
193 "111",
194 ])
195 .unwrap();
196 assert_eq!(
197 matches.get_one::<String>("const_val_1"),
198 Some(&String::from("2333"))
199 );
200 assert_eq!(
201 matches.get_one::<String>("const_val_2"),
202 Some(&String::from("12345678"))
203 );
204 assert_eq!(
205 matches.get_one::<String>("const_val_3"),
206 Some(&String::from("abcdefg"))
207 );
208 assert_eq!(
209 matches.get_one::<String>("bss_val_1"),
210 Some(&String::from("111"))
211 );
212 assert_eq!(matches.get_one::<String>("bss_val_2"), None);
213 assert_eq!(matches.get_one::<String>("bss_val_3"), None);
214 }
215 #[test]
216 #[should_panic]
217 fn test_arg_builder_invalid_argument() {
218 let skel = serde_json::from_str::<EunomiaObjectMeta>(
219 &std::fs::read_to_string(get_assets_dir().join("arg_builder_test").join("skel.json"))
220 .unwrap(),
221 )
222 .unwrap();
223 let cmd = skel.build_argument_parser().unwrap();
224 cmd.try_get_matches_from(["prog", "-a", "123"]).unwrap();
225 }
226 #[test]
227 fn test_boolflag() {
228 let skel = serde_json::from_str::<EunomiaObjectMeta>(
229 &std::fs::read_to_string(get_assets_dir().join("arg_builder_test").join("skel.json"))
230 .unwrap(),
231 )
232 .unwrap();
233 let cmd = skel.build_argument_parser().unwrap();
234 let matches = cmd
235 .clone()
236 .try_get_matches_from([
237 "prog",
238 "--boolflag",
239 "--disable-boolflag-with-default-true",
240 "--enable-boolflag-with-default-false",
241 ])
242 .unwrap();
243 assert!(matches.get_flag("boolflag"));
244 assert!(!matches.get_flag("boolflag-with-default-true"));
245 assert!(matches.get_flag("boolflag-with-default-false"));
246
247 let matches = cmd.clone().try_get_matches_from(["prog"]).unwrap();
248 assert!(!matches.get_flag("boolflag"));
249 assert!(matches.get_flag("boolflag-with-default-true"));
250 assert!(!matches.get_flag("boolflag-with-default-false"));
251 }
252 #[test]
253 #[should_panic]
254 fn test_boolflag_2() {
255 let skel = serde_json::from_str::<EunomiaObjectMeta>(
256 &std::fs::read_to_string(get_assets_dir().join("arg_builder_test").join("skel.json"))
257 .unwrap(),
258 )
259 .unwrap();
260 let cmd = skel.build_argument_parser().unwrap();
261 cmd.clone()
262 .try_get_matches_from(["prog", "--enable-boolflag-with-default-true"])
263 .unwrap();
264 }
265 #[test]
266 #[should_panic]
267 fn test_boolflag_3() {
268 let skel = serde_json::from_str::<EunomiaObjectMeta>(
269 &std::fs::read_to_string(get_assets_dir().join("arg_builder_test").join("skel.json"))
270 .unwrap(),
271 )
272 .unwrap();
273 let cmd = skel.build_argument_parser().unwrap();
274 cmd.clone()
275 .try_get_matches_from(["prog", "--disable-boolflag-with-default-false"])
276 .unwrap();
277 }
278}