1#[derive(Clone, Copy, Debug, PartialEq)]
2pub enum Span {
3 Exact(usize), FirstArg,
5 End,
6 NthArg(usize), None,
8}
9
10#[derive(Clone, Debug)]
11pub struct RenderedSpan {
12 pub args: String,
13 pub underline_start: usize,
14 pub underline_end: usize,
15}
16
17impl Span {
18 pub fn render(&self, args: &[String], skip_first_n: usize) -> Option<RenderedSpan> {
19 if let Span::None = self {
20 return None;
21 }
22
23 let mut rendered_args = Vec::with_capacity(args.len());
24 let mut arg_indices = vec![];
25
26 for (index, arg) in args.iter().enumerate() {
27 if !arg.starts_with("--") && index >= skip_first_n {
28 arg_indices.push(index);
29 }
30
31 if arg.contains(" ") || arg.contains("\"") || arg.contains("'") || arg.contains("\n") {
32 rendered_args.push(format!("{arg:?}"));
33 }
34
35 else {
36 rendered_args.push(arg.to_string());
37 }
38 }
39
40 let new_span = match self {
41 Span::Exact(n) => Span::Exact(*n),
42 Span::FirstArg => match arg_indices.get(0) {
43 Some(n) => Span::Exact(*n),
44 None => Span::End,
45 },
46 Span::NthArg(n) => match arg_indices.get(*n) {
47 Some(n) => Span::Exact(*n),
48 None => Span::End,
49 },
50 _ => self.clone(),
51 };
52 let selected_index = match new_span {
53 Span::Exact(n) => n,
54 _ => 0,
55 };
56 let mut joined_args = rendered_args.join(" ");
57 let (start, end) = if joined_args.is_empty() {
58 joined_args = String::from(" ");
59 (0, 1)
60 } else {
61 joined_args = format!("{joined_args} ");
63
64 match new_span {
65 Span::End => (joined_args.len() - 1, joined_args.len()),
66 _ => (
67 rendered_args[..selected_index].iter().map(|arg| arg.len()).sum::<usize>() + selected_index,
68 rendered_args[..(selected_index + 1)].iter().map(|arg| arg.len()).sum::<usize>() + selected_index,
69 ),
70 }
71 };
72
73 Some(RenderedSpan {
74 args: joined_args,
75 underline_start: start,
76 underline_end: end,
77 })
78 }
79}
80
81pub fn underline_span(s: &RenderedSpan) -> String {
82 format!(
83 "{}\n{}{}{}",
84 s.args,
85 " ".repeat(s.underline_start),
86 "^".repeat(s.underline_end - s.underline_start),
87 " ".repeat(s.args.len() - s.underline_end),
88 )
89}