1use std::vec::Vec;
2
3use crate::{endpoint, Ctx, EndpointHandle};
4use colored::Colorize;
5
6#[derive(Default)]
7enum UsageState {
8 #[default]
9 NotUsing,
10 Using,
11 UsingHiddenChild,
12}
13
14struct Output {
15 method: Option<String>,
16 handle: String,
17 has_more: bool,
18 usage: UsageState,
19}
20
21impl Output {
22 fn builder() -> OutputBuilder {
23 OutputBuilder::default()
24 }
25}
26
27#[derive(Default)]
28struct OutputBuilder {
29 method: Option<String>,
30 handle: Option<String>,
31 has_more: bool,
32 usage: UsageState,
33}
34
35impl OutputBuilder {
36 fn method(&mut self, method: String) -> &mut Self {
37 self.method = Some(method);
38 self
39 }
40
41 fn handle(&mut self, handle: String) -> &mut Self {
42 self.handle = Some(handle);
43 self
44 }
45
46 fn has_more(&mut self, has_more: bool) -> &mut Self {
47 self.has_more = has_more;
48 self
49 }
50
51 fn usage(&mut self, usage: UsageState) -> &mut Self {
52 self.usage = usage;
53 self
54 }
55
56 fn build(self) -> Result<Output, ()> {
57 let handle = self.handle.ok_or(())?;
58
59 if handle.is_empty() {
60 return Err(());
61 }
62
63 Ok(Output {
64 method: self.method,
65 handle,
66 has_more: self.has_more,
67 usage: self.usage,
68 })
69 }
70}
71
72fn print_outputs(list: Vec<Output>) {
73 let padding = list
74 .iter()
75 .map(|e| e.method.as_ref().unwrap_or(&"---".to_string()).len())
76 .fold(0, |l, e| l.max(e));
77
78 for output in list {
79 let (usage_mark, name) = match output.usage {
80 UsageState::NotUsing => (" ", output.handle.normal()),
81 UsageState::Using => ("*", output.handle.green()),
82 UsageState::UsingHiddenChild => ("*", output.handle.normal()),
83 };
84
85 let more_info = if output.has_more {
86 " +".dimmed()
87 } else {
88 "".normal()
89 };
90
91 let method = if let Some(method) = output.method {
92 endpoint::colored_method(&method).bold()
93 } else {
94 "---".dimmed()
95 };
96
97 println!(
98 "{} {:<padding$} {}{}",
99 usage_mark,
100 method,
101 name,
102 more_info,
103 padding = padding
104 );
105 }
106}
107
108#[derive(clap::Args, Debug)]
109pub struct Args {
110 handle: Option<String>,
112
113 #[arg(long, value_name = "N")]
115 depth: Option<usize>,
116}
117
118pub fn cmd(ctx: &Ctx, args: Args) {
119 let max_depth = args.depth.unwrap_or(usize::MAX).max(1);
120 let active_handle = EndpointHandle::from_state(ctx);
121 let mut output_list: Vec<Output> = vec![];
122
123 let tree = if let Some(name) = args.handle {
124 let handle = EndpointHandle::from(&name);
125
126 if !handle.exists(ctx) {
127 panic!("no such handle: {name}");
128 }
129
130 handle.tree(ctx)
131 } else {
132 EndpointHandle::QUARTZ.tree(ctx)
133 };
134
135 let mut queue = vec![&tree.root];
136
137 while let Some(node) = queue.pop() {
138 let mut builder = Output::builder();
139
140 builder.handle(node.value.handle());
141
142 if let Some(endpoint) = node.value.endpoint(ctx) {
143 builder.method(endpoint.method);
144 } else {
145 builder.method("---".into());
146 }
147
148 if let Some(active_handle) = &active_handle {
149 if node.value.handle() == active_handle.handle() {
150 builder.usage(UsageState::Using);
151 }
152 }
153
154 if node.value.depth() > max_depth {
155 if node.children.is_empty() {
156 builder.has_more(true);
157
158 if let Some(active_handle) = &active_handle {
159 if active_handle.handle().starts_with(&node.value.handle()) {
160 builder.usage(UsageState::UsingHiddenChild);
161 }
162 }
163 }
164
165 continue;
166 }
167
168 if let Ok(output) = builder.build() {
169 output_list.push(output);
170 }
171
172 for child in node.children.iter() {
173 queue.push(&child);
174 }
175 }
176
177 print_outputs(output_list);
178}