quartz_cli/action/
ls.rs

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    /// See the children of a specific handle
111    handle: Option<String>,
112
113    /// Set a limit for how deep the listing goes in sub-handles
114    #[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}