1use kdl::KdlNode;
2use std::{
3 collections::{HashMap, HashSet},
4 io,
5};
6
7use crate::namespace::NameSpace;
8
9#[derive(Debug, Default, Clone)]
10pub struct UsageSpec {
11 pub info: Info,
12 pub flags: Vec<Flag>,
13 pub args: Vec<Arg>,
14 pub cmds: Vec<Cmd>,
15 pub completes: HashMap<String, Complete>,
16}
17
18#[derive(Debug, Default, Clone)]
19pub struct Info {
20 pub name: String,
21 pub bin: String,
22}
23
24#[derive(Debug, Clone)]
25pub enum Usage {
26 Flag(Flag),
27 Arg(Arg),
28 Cmd(Cmd),
29 Complete(Complete),
30}
31
32#[derive(Debug, Default, Clone)]
33pub struct Alias {
34 pub name: String,
35 pub hide: bool,
36}
37
38#[derive(Debug, Default, Clone)]
39pub enum GlobalFlag {
40 #[default]
41 None,
42 Itself,
43 Imposed(NameSpace),
44}
45
46#[derive(Debug, Default, Clone)]
47pub struct Flag {
48 pub name: String,
49 pub names: Vec<String>,
50 pub help: String,
51 pub hide: bool,
52 pub global: GlobalFlag,
53 pub aliases: Vec<Alias>,
54 pub arg: Option<Arg>,
55}
56
57#[derive(Debug, Default, Clone)]
58pub struct Arg {
59 pub name: String,
60 pub repr: String,
61 pub required: bool,
62 pub choices: Vec<String>,
63 pub hide: bool,
64 pub var: bool,
65 pub min: Option<i128>,
66 pub max: Option<i128>,
67 pub default: Option<String>,
68}
69
70#[derive(Debug, Default, Clone)]
71pub struct Cmd {
72 pub name: String,
73 pub help: String,
74 pub hide: bool,
75 pub args: Vec<Arg>,
76 pub flags: Vec<Flag>,
77 pub aliases: Vec<Alias>,
78 pub cmds: Vec<Box<Cmd>>,
79}
80
81#[derive(Debug, Default, Clone)]
82pub struct Complete {
83 pub name: String,
84 pub kind: CompleteKind,
85 pub descs: bool,
86}
87
88#[derive(Debug, Clone)]
89pub enum CompleteKind {
90 None,
91 File,
92 Dir,
93 Run(String),
94}
95
96pub fn parse_name(node: &KdlNode) -> Result<String, UError> {
97 if node.name().value() != "name" {
98 return Err(UError::InvalidNodeName(io::Error::new(
99 io::ErrorKind::InvalidInput,
100 format!("Node name wasn't name!\n{:?}", node),
101 )));
102 }
103 let name = node
104 .get(0)
105 .map(|v| v.as_string().unwrap_or_default().to_string())
106 .ok_or_else(|| {
107 UError::InvalidNodeFirstArg(io::Error::new(
108 io::ErrorKind::NotFound,
109 format!("No name found in {:?}", node),
110 ))
111 })?;
112 Ok(name)
113}
114
115pub fn parse_bin(node: &KdlNode) -> Result<String, UError> {
116 if node.name().value() != "bin" {
117 return Err(UError::InvalidNodeName(io::Error::new(
118 io::ErrorKind::InvalidInput,
119 format!("Node name wasn't bin!\n{:?}", node),
120 )));
121 }
122 let bin = node
123 .get(0)
124 .map(|v| v.as_string().unwrap_or_default().to_string())
125 .ok_or_else(|| {
126 UError::InvalidNodeFirstArg(io::Error::new(
127 io::ErrorKind::NotFound,
128 format!("No bin found in {:?}", node),
129 ))
130 })?;
131 Ok(bin)
132}
133
134pub fn parse_include(node: &KdlNode) -> Result<String, UError> {
135 if node.name().value() != "include" {
136 return Err(UError::InvalidNodeName(io::Error::new(
137 io::ErrorKind::InvalidInput,
138 format!("Node name wasn't include!\n{:?}", node),
139 )));
140 }
141 let include = node
142 .get(0)
143 .map(|v| v.as_string().unwrap_or_default().to_string())
144 .ok_or_else(|| {
145 UError::InvalidNodeFirstArg(io::Error::new(
146 io::ErrorKind::NotFound,
147 format!("No include found in {:?}", node),
148 ))
149 })?;
150 Ok(include)
151}
152
153pub fn parse_alias(node: &KdlNode) -> Result<Vec<Alias>, UError> {
154 if node.name().value() != "alias" {
155 return Err(UError::InvalidNodeName(io::Error::new(
156 io::ErrorKind::InvalidInput,
157 format!("Node name wasn't alias!\n{:?}", node),
158 )));
159 }
160
161 let mut aliases: Vec<Alias> = vec![];
162 for entry in node.entries() {
163 let mut hide = false;
164 if entry.name().is_none() {
165 let alias_name = entry
166 .value()
167 .as_string()
168 .ok_or_else(|| {
169 UError::InvalidNodeFirstArg(io::Error::new(
170 io::ErrorKind::NotFound,
171 format!("No alias found in {:?}", entry),
172 ))
173 })?
174 .to_string();
175 if let Some(hide_val) = node.get("hide") {
176 hide = hide_val.as_bool().unwrap_or_default();
177 }
178 let alias = Alias {
179 name: alias_name,
180 hide,
181 };
182 aliases.push(alias);
183 }
184 }
185 Ok(aliases)
186}
187
188pub fn parse_choices(node: &KdlNode) -> Result<Vec<String>, UError> {
189 if node.name().value() != "choices" {
190 return Err(UError::InvalidNodeName(io::Error::new(
191 io::ErrorKind::InvalidInput,
192 format!("Node name wasn't choices!\n{:?}", node),
193 )));
194 }
195
196 let mut choices: Vec<String> = vec![];
197 for entry in node.entries() {
198 let choice = entry
199 .value()
200 .as_string()
201 .ok_or_else(|| {
202 UError::InvalidNodeFirstArg(io::Error::new(
203 io::ErrorKind::NotFound,
204 format!("No choice found in {:?}", entry),
205 ))
206 })?
207 .to_string();
208 choices.push(choice);
209 }
210 Ok(choices)
211}
212
213pub fn parse_flag(node: &KdlNode) -> Result<Flag, UError> {
214 if node.name().value() != "flag" {
215 return Err(UError::InvalidNodeName(io::Error::new(
216 io::ErrorKind::InvalidInput,
217 format!("Node name wasn't flag!\n{:?}", node),
218 )));
219 }
220
221 let mut flag = Flag::default();
222 for (index, entry) in node.entries().iter().enumerate() {
223 if index == 0 {
224 let entry_flag_names = entry
225 .value()
226 .as_string()
227 .ok_or_else(|| {
228 UError::InvalidNodeFirstArg(io::Error::new(
229 io::ErrorKind::NotFound,
230 format!("No flag found in {:?}", entry),
231 ))
232 })?
233 .to_string();
234
235 let (long_flag_index, flag_names) = {
237 let mut name_len = 0;
238 let mut flag_index = 0;
239 let mut long_flag_index = flag_index;
240 let flag_names: Vec<String> = entry_flag_names
241 .split_whitespace()
242 .map(|s| {
243 let s = String::from(s);
244 let len = s.len();
245 if len > name_len {
246 name_len = len;
247 long_flag_index = flag_index;
248 };
249 flag_index += 1;
250 s
251 })
252 .collect();
253 (long_flag_index, flag_names)
254 };
255
256 let slugify = |mut c: char| {
257 if !c.is_alphanumeric() && c != '_' {
258 c = '_';
259 }
260 c
261 };
262
263 let flag_name = flag_names[long_flag_index]
264 .trim_matches('-')
265 .chars()
266 .map(slugify)
267 .collect();
268
269 flag.name = flag_name;
270 flag.names = flag_names;
271 }
272
273 if let Some(iden_name) = entry.name() {
274 match iden_name.value() {
275 "help" => flag.help = entry.value().as_string().unwrap_or_default().to_string(),
276 "hide" => flag.hide = entry.value().as_bool().unwrap_or_default(),
277 "global" => flag.global = entry.value().as_bool().unwrap_or_default().into(),
278 "negate" => {
279 let negate_flag = entry.value().as_string().unwrap_or_default().to_string();
280 if !negate_flag.is_empty() {
281 flag.names.push(negate_flag);
282 }
283 }
284 _ => {}
285 }
286 }
287 }
288
289 if let Some(child_doc) = node.children() {
290 for child_node in child_doc.nodes() {
291 match child_node.name().value() {
292 "arg" => flag.arg = Some(parse_arg(child_node)?),
293 "alias" => flag.aliases = parse_alias(child_node)?,
294 "choices" => {
295 if let Some(arg_name) = flag.names.pop() {
296 let mut arg = Arg::default();
297 arg.name = arg_name;
298 arg.choices = parse_choices(child_node)?;
299 if arg.name.starts_with("<") {
300 arg.required = true;
301 }
302 flag.arg = Some(arg);
303 }
304 }
305 _ => {}
306 }
307 }
308 }
309 Ok(flag)
310}
311
312pub fn parse_arg(node: &KdlNode) -> Result<Arg, UError> {
313 if node.name().value() != "arg" {
314 return Err(UError::InvalidNodeName(io::Error::new(
315 io::ErrorKind::InvalidInput,
316 format!("Node name wasn't arg!\n{:?}", node),
317 )));
318 }
319
320 let mut arg = Arg::default();
321 for (index, entry) in node.entries().iter().enumerate() {
322 if index == 0 {
323 let entry_arg_name = entry
324 .value()
325 .as_string()
326 .ok_or_else(|| {
327 UError::InvalidNodeFirstArg(io::Error::new(
328 io::ErrorKind::NotFound,
329 format!("No arg found in {:?}", entry),
330 ))
331 })?
332 .to_string();
333
334 if entry_arg_name.starts_with("<") {
335 arg.required = true;
336 let end = entry_arg_name.find(">").unwrap_or(entry_arg_name.len());
337 arg.name = entry_arg_name[1..end].to_string();
338 } else if entry_arg_name.starts_with("[") {
339 arg.required = false;
340 let end = entry_arg_name.find("]").unwrap_or(entry_arg_name.len());
341 arg.name = entry_arg_name[1..end].to_string();
342 }
343 arg.repr = entry_arg_name;
344 }
345
346 if let Some(iden_name) = entry.name() {
347 match iden_name.value() {
348 "hide" => arg.hide = entry.value().as_bool().unwrap_or_default(),
349 "default" => arg.default = entry.value().as_string().map(String::from),
350 "var" => arg.var = entry.value().as_bool().unwrap_or_default(),
351 "var_max" => arg.max = entry.value().as_integer(),
352 "var_min" => arg.min = entry.value().as_integer(),
353 _ => {}
354 }
355 }
356 }
357
358 arg.max = arg.max.or(Some(-1));
359 arg.min = arg.min.or(Some(0));
360
361 if let Some(child_doc) = node.children() {
362 for child_node in child_doc.nodes() {
363 if child_node.name().value() == "choices" {
364 let mut choices: Vec<String> = vec![];
365 for cn_entry in child_node.entries() {
366 let choice = cn_entry
367 .value()
368 .as_string()
369 .expect(format!("No choice found in {:?}", cn_entry).as_str())
370 .to_string();
371 choices.push(choice);
372 }
373 arg.choices = choices;
374 }
375 }
376 }
377 Ok(arg)
378}
379
380pub fn parse_cmd(node: &KdlNode) -> Result<Cmd, UError> {
381 if node.name().value() != "cmd" {
382 return Err(UError::InvalidNodeName(io::Error::new(
383 io::ErrorKind::InvalidInput,
384 format!("Node name wasn't cmd!\n{:?}", node),
385 )));
386 }
387
388 let mut cmd = Cmd::default();
389 for (index, entry) in node.entries().iter().enumerate() {
390 if index == 0 {
391 let entry_cmd_name = entry
392 .value()
393 .as_string()
394 .ok_or_else(|| {
395 UError::InvalidNodeFirstArg(io::Error::new(
396 io::ErrorKind::NotFound,
397 format!("No cmd found in {:?}", entry),
398 ))
399 })?
400 .to_string();
401
402 cmd.name = entry_cmd_name;
403 }
404
405 if let Some(iden_name) = entry.name() {
406 match iden_name.value() {
407 "help" => {
408 cmd.help = entry
409 .value()
410 .as_string()
411 .map(String::from)
412 .unwrap_or_default()
413 }
414 "hide" => cmd.hide = entry.value().as_bool().unwrap_or_default(),
415 _ => {}
416 }
417 }
418 }
419
420 if let Some(child_doc) = node.children() {
421 for child_node in child_doc.nodes() {
422 match child_node.name().value() {
423 "alias" => {
424 let mut alias = parse_alias(child_node)?;
425 cmd.aliases.append(&mut alias);
426 }
427 "flag" => {
428 let flag = parse_flag(child_node)?;
429 cmd.flags.push(flag);
430 }
431 "arg" => {
432 let arg = parse_arg(child_node)?;
433 cmd.args.push(arg);
434 }
435 "cmd" => {
436 let child_cmd = parse_cmd(child_node)?;
437 cmd.cmds.push(Box::new(child_cmd));
438 }
439 _ => {}
440 }
441 }
442 }
443 Ok(cmd)
444}
445
446pub fn parse_complete(node: &KdlNode) -> Result<Complete, UError> {
447 if node.name().value() != "complete" {
448 return Err(UError::InvalidNodeName(io::Error::new(
449 io::ErrorKind::InvalidInput,
450 format!("Node name wasn't complete!\n{:?}", node),
451 )));
452 }
453
454 let mut complete = Complete::default();
455 for (index, entry) in node.entries().iter().enumerate() {
456 if index == 0 {
457 let entry_complete_name = entry
458 .value()
459 .as_string()
460 .ok_or_else(|| {
461 UError::InvalidNodeFirstArg(io::Error::new(
462 io::ErrorKind::NotFound,
463 format!("No complete found in {:?}", entry),
464 ))
465 })?
466 .to_string();
467 complete.name = entry_complete_name;
468 }
469
470 if let Some(iden_name) = entry.name() {
471 match iden_name.value() {
472 "descriptions" => complete.descs = entry.value().as_bool().unwrap_or_default(),
473 "run" => {
474 let run = entry
475 .value()
476 .as_string()
477 .map(String::from)
478 .unwrap_or_default();
479 complete.kind = CompleteKind::Run(run);
480 }
481 "type" => {
482 let arg_type = entry.value().as_string().unwrap_or_default();
483 match arg_type {
484 "file" => complete.kind = CompleteKind::File,
485 _ => {}
486 }
487 }
488 _ => {}
489 }
490 }
491 }
492 Ok(complete)
493}
494
495pub fn parse_usage(node: &KdlNode) -> Result<Option<Usage>, UError> {
496 match node.name().value() {
497 "flag" => Ok(Some(Usage::Flag(parse_flag(node)?))),
498 "arg" => Ok(Some(Usage::Arg(parse_arg(node)?))),
499 "cmd" => Ok(Some(Usage::Cmd(parse_cmd(node)?))),
500 "complete" => {
501 let complete = parse_complete(node)?;
502 if !complete.kind.is_none() {
503 Ok(Some(Usage::Complete(complete)))
504 } else {
505 Ok(None)
506 }
507 }
508 _ => Ok(None),
509 }
510}
511
512impl Complete {
513 pub fn file_complete() -> Self {
514 Self {
515 name: "file".to_string(),
516 kind: CompleteKind::File,
517 descs: false,
518 }
519 }
520
521 pub fn dir_complete() -> Self {
522 Self {
523 name: "file".to_string(),
524 kind: CompleteKind::Dir,
525 descs: false,
526 }
527 }
528}
529
530impl PartialEq for Flag {
531 fn eq(&self, other: &Self) -> bool {
532 self.name == other.name && self.names.len() == other.names.len() && {
533 let a: HashSet<_> = self.names.iter().collect();
534 let b: HashSet<_> = other.names.iter().collect();
535 a == b
536 }
537 }
538}
539impl Eq for Flag {}
540
541impl PartialEq for Arg {
542 fn eq(&self, other: &Self) -> bool {
543 self.name == other.name
544 }
545}
546impl Eq for Arg {}
547
548impl PartialEq for Cmd {
549 fn eq(&self, other: &Self) -> bool {
550 self.name == other.name
551 }
552}
553impl Eq for Cmd {}
554
555impl PartialEq for Complete {
556 fn eq(&self, other: &Self) -> bool {
557 self.name == other.name
558 }
559}
560impl Eq for Complete {}
561
562impl CompleteKind {
563 pub fn is_none(&self) -> bool {
564 match self {
565 Self::None => true,
566 _ => false,
567 }
568 }
569
570 pub fn is_file(&self) -> bool {
571 match self {
572 Self::File => true,
573 _ => false,
574 }
575 }
576
577 pub fn run(&self) -> Option<&String> {
578 match self {
579 Self::Run(run) => Some(run),
580 _ => None,
581 }
582 }
583}
584
585#[derive(Debug)]
586pub enum UError {
587 InvalidNodeName(io::Error),
588 InvalidNodeFirstArg(io::Error),
589}
590
591impl std::fmt::Display for UError {
592 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
593 match self {
594 UError::InvalidNodeName(error) => error.fmt(f),
595 UError::InvalidNodeFirstArg(error) => error.fmt(f),
596 }
597 }
598}
599
600impl std::error::Error for UError {}
601
602impl From<UError> for io::Error {
603 fn from(value: UError) -> Self {
604 match value {
605 UError::InvalidNodeName(error) => error,
606 UError::InvalidNodeFirstArg(error) => error,
607 }
608 }
609}
610
611impl AsRef<Cmd> for Cmd {
612 fn as_ref(&self) -> &Cmd {
613 self
614 }
615}
616
617impl Default for CompleteKind {
618 fn default() -> Self {
619 Self::None
620 }
621}
622
623impl From<bool> for GlobalFlag {
624 fn from(value: bool) -> Self {
625 match value {
626 true => Self::Itself,
627 false => Self::None,
628 }
629 }
630}
631
632impl Flag {
633 pub fn is_global(&self) -> bool {
634 match self.global {
635 GlobalFlag::None => false,
636 _ => true,
637 }
638 }
639
640 pub fn is_global_itself(&self) -> bool {
641 match self.global {
642 GlobalFlag::Itself => true,
643 _ => false,
644 }
645 }
646
647 pub fn is_global_imposed(&self) -> bool {
648 match self.global {
649 GlobalFlag::Imposed(_) => true,
650 _ => false,
651 }
652 }
653}