Skip to main content

faucet_cli/commands/
list.rs

1//! `faucet list` — show every compiled-in source, sink, transform, and
2//! state-store backend so users can discover what their binary supports.
3//! `--available` instead lists every connector in the registry index (#208),
4//! marking which are compiled into this binary.
5
6use crate::cli::ListArgs;
7use crate::conformance::tier_for;
8use crate::error::CliResult;
9use crate::registry::{sink_descriptions, sink_exists, source_descriptions, source_exists};
10use crate::registry_index::RegistryIndex;
11use crate::state::available_state_kinds;
12#[cfg(feature = "quality")]
13use crate::transforms::quality_descriptions;
14use crate::transforms::transform_descriptions;
15
16/// Execute the `list` subcommand.
17pub async fn run(args: ListArgs) -> CliResult<()> {
18    if args.available {
19        return list_available(args);
20    }
21    println!("Sources:");
22    print_connectors(&source_descriptions(), true);
23    println!();
24    println!("Sinks:");
25    print_connectors(&sink_descriptions(), false);
26    println!();
27    println!("Transforms:");
28    print_two_column(&transform_descriptions());
29    println!();
30    #[cfg(feature = "quality")]
31    {
32        println!("Quality checks:");
33        print_two_column(&quality_descriptions());
34        println!();
35    }
36    println!("State stores: {}", available_state_kinds().join(", "));
37    #[cfg(feature = "schedule")]
38    println!("Scheduler:    compiled in (run `faucet schedule --help`, `faucet schema schedule`)");
39    Ok(())
40}
41
42/// `faucet list --available` — every connector in the registry index, with a
43/// marker for those already compiled into this binary.
44fn list_available(args: ListArgs) -> CliResult<()> {
45    let idx = RegistryIndex::load(args.index.as_deref())?;
46    let mut connectors: Vec<_> = idx.connectors.iter().collect();
47    connectors.sort_by(|a, b| {
48        (a.kind.as_str(), a.name.as_str()).cmp(&(b.kind.as_str(), b.name.as_str()))
49    });
50    println!(
51        "Registry connectors ({} total). ● = compiled into this binary, ○ = available via `faucet install`:\n",
52        connectors.len()
53    );
54    for c in connectors {
55        let compiled = match c.kind.as_str() {
56            "source" => source_exists(&c.name),
57            "sink" => sink_exists(&c.name),
58            _ => false,
59        };
60        let mark = if compiled { '●' } else { '○' };
61        let badge = if c.verified { "verified" } else { "community" };
62        let tier = c.tier.as_deref().unwrap_or("-");
63        println!(
64            "  {mark} {kind:<6} {name:<14} {tier:<12} {desc}  [{badge}]",
65            kind = c.kind,
66            name = c.name,
67            desc = c.description
68        );
69    }
70    Ok(())
71}
72
73fn print_two_column(entries: &[(&'static str, &'static str)]) {
74    if entries.is_empty() {
75        println!("  (none — rebuild faucet-cli with the relevant features enabled)");
76        return;
77    }
78    let width = entries.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
79    for (name, desc) in entries {
80        println!("  {name:<width$}  {desc}", width = width);
81    }
82}
83
84/// Like [`print_two_column`] but prefixes each connector with its conformance
85/// maturity tier badge (`faucet conformance` for the full scorecards).
86fn print_connectors(entries: &[(&'static str, &'static str)], is_source: bool) {
87    if entries.is_empty() {
88        println!("  (none — rebuild faucet-cli with the relevant features enabled)");
89        return;
90    }
91    let width = entries.iter().map(|(n, _)| n.len()).max().unwrap_or(0);
92    for (name, desc) in entries {
93        let tier = tier_for(name, is_source);
94        println!(
95            "  {badge} {name:<width$}  {tier:<12} {desc}",
96            badge = tier.badge(),
97            tier = tier.label(),
98            width = width,
99        );
100    }
101}