wash_cli/common/
get_cmd.rs

1use anyhow::Result;
2use crossterm::{
3    cursor, execute,
4    terminal::{Clear, ClearType},
5};
6use std::{collections::HashMap, io::Write, time::Duration};
7use tokio::time::sleep;
8use wash_lib::cli::claims::get_claims;
9use wash_lib::cli::get::{
10    get_host_inventories, get_hosts, GetCommand, GetHostInventoriesCommand, GetLinksCommand,
11};
12use wash_lib::cli::link::{LinkCommand, LinkQueryCommand};
13use wash_lib::cli::{CommandOutput, OutputKind};
14
15use crate::appearance::spinner::Spinner;
16use crate::cmd::link::invoke as invoke_link_cmd;
17use crate::ctl::{
18    get_claims_output, get_host_inventories_output, get_hosts_output, host_inventories_table,
19};
20
21pub async fn handle_command(command: GetCommand, output_kind: OutputKind) -> Result<CommandOutput> {
22    let out: CommandOutput = match command {
23        GetCommand::Links(GetLinksCommand { opts }) => {
24            invoke_link_cmd(LinkCommand::Query(LinkQueryCommand { opts }), output_kind).await?
25        }
26        GetCommand::Claims(cmd) => {
27            let sp: Spinner = Spinner::new(&output_kind)?;
28            sp.update_spinner_message("Retrieving claims ... ".to_string());
29            let claims = get_claims(cmd).await?;
30            get_claims_output(claims)
31        }
32        GetCommand::Hosts(cmd) => {
33            let sp: Spinner = Spinner::new(&output_kind)?;
34            sp.update_spinner_message(" Retrieving Hosts ...".to_string());
35            let hosts = get_hosts(cmd).await?;
36            get_hosts_output(hosts)
37        }
38        GetCommand::HostInventories(cmd) => {
39            let sp: Spinner = Spinner::new(&output_kind)?;
40            if let Some(id) = cmd.host_id.as_ref() {
41                sp.update_spinner_message(format!(" Retrieving inventory for host {} ...", id));
42            } else {
43                sp.update_spinner_message(" Retrieving hosts for inventory query ...".to_string());
44            }
45            get_inventory_handler(cmd, sp).await?
46        }
47    };
48
49    Ok(out)
50}
51
52async fn get_inventory_handler(
53    cmd: GetHostInventoriesCommand,
54    sp: Spinner,
55) -> Result<CommandOutput> {
56    if cmd.watch.is_some() {
57        watch_inventory(cmd, sp).await?;
58        Ok(CommandOutput::new(
59            "Completed Watching Inventory".to_string(),
60            HashMap::new(),
61        ))
62    } else {
63        let invs = get_host_inventories(cmd).await?;
64        Ok(get_host_inventories_output(invs))
65    }
66}
67
68async fn watch_inventory(cmd: GetHostInventoriesCommand, sp: Spinner) -> Result<()> {
69    let mut stdout = std::io::stdout();
70    let invs = get_host_inventories(cmd.clone()).await?;
71    sp.finish_and_clear();
72    execute!(stdout, Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0))
73        .map_err(|e| anyhow::anyhow!("Failed to clear terminal: {}", e))?;
74    let output = host_inventories_table(invs);
75    stdout
76        .write_all(output.as_bytes())
77        .map_err(|e| anyhow::anyhow!("Failed to write inventory to stdout: {}", e))?;
78
79    let mut ctrlc = std::pin::pin!(tokio::signal::ctrl_c());
80    let watch_interval = cmd.watch.unwrap_or(Duration::from_millis(5000));
81
82    loop {
83        let invs = tokio::select! {
84            res = get_host_inventories(cmd.clone()) => res?,
85            res = &mut ctrlc => {
86                res?;
87                execute!(stdout, Clear(ClearType::Purge),Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0), cursor::Show)
88                    .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;
89                stdout.flush()
90                    .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;
91                return Ok(());
92            }
93        };
94
95        execute!(stdout, Clear(ClearType::Purge), cursor::MoveTo(0, 0))
96            .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;
97
98        let output = host_inventories_table(invs);
99        stdout
100            .write_all(output.as_bytes())
101            .map_err(|e| anyhow::anyhow!("Failed to write inventory to stdout: {}", e))?;
102
103        stdout
104            .flush()
105            .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;
106
107        execute!(
108            stdout,
109            Clear(ClearType::CurrentLine),
110            Clear(ClearType::FromCursorDown),
111        )
112        .map_err(|e| anyhow::anyhow!("Failed to clear terminal: {}", e))?;
113
114        tokio::select! {
115            _ = sleep(watch_interval) => continue,
116            res = &mut ctrlc => {
117                res?;
118                execute!(stdout, Clear(ClearType::Purge),Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0), cursor::Show)
119                    .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;
120                stdout.flush()
121                    .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;
122                return Ok(());
123            }
124        }
125    }
126}