1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use anyhow::Result;
use crossterm::{
    cursor, execute,
    terminal::{Clear, ClearType},
};
use std::{collections::HashMap, io::Write, time::Duration};
use tokio::time::sleep;
use wash_lib::cli::claims::get_claims;
use wash_lib::cli::get::{
    get_host_inventories, get_hosts, GetCommand, GetHostInventoriesCommand, GetLinksCommand,
};
use wash_lib::cli::link::{LinkCommand, LinkQueryCommand};
use wash_lib::cli::{CommandOutput, OutputKind};

use crate::appearance::spinner::Spinner;
use crate::common::link_cmd::handle_command as handle_link_command;
use crate::ctl::{
    get_claims_output, get_host_inventories_output, get_hosts_output, host_inventories_table,
};

pub async fn handle_command(command: GetCommand, output_kind: OutputKind) -> Result<CommandOutput> {
    let sp: Spinner = Spinner::new(&output_kind)?;
    let out: CommandOutput = match command {
        GetCommand::Links(GetLinksCommand { opts }) => {
            handle_link_command(LinkCommand::Query(LinkQueryCommand { opts }), output_kind).await?
        }
        GetCommand::Claims(cmd) => {
            sp.update_spinner_message("Retrieving claims ... ".to_string());
            let claims = get_claims(cmd).await?;
            get_claims_output(claims)
        }
        GetCommand::Hosts(cmd) => {
            sp.update_spinner_message(" Retrieving Hosts ...".to_string());
            let hosts = get_hosts(cmd).await?;
            get_hosts_output(hosts)
        }
        GetCommand::HostInventories(cmd) => {
            if let Some(id) = cmd.host_id.as_ref() {
                sp.update_spinner_message(format!(" Retrieving inventory for host {} ...", id));
            } else {
                sp.update_spinner_message(" Retrieving hosts for inventory query ...".to_string());
            }
            get_inventory_handler(cmd, sp).await?
        }
    };

    Ok(out)
}

async fn get_inventory_handler(
    cmd: GetHostInventoriesCommand,
    sp: Spinner,
) -> Result<CommandOutput> {
    if cmd.watch.is_some() {
        watch_inventory(cmd, sp).await?;
        Ok(CommandOutput::new(
            "Completed Watching Inventory".to_string(),
            HashMap::new(),
        ))
    } else {
        let invs = get_host_inventories(cmd).await?;
        Ok(get_host_inventories_output(invs))
    }
}

async fn watch_inventory(cmd: GetHostInventoriesCommand, sp: Spinner) -> Result<()> {
    let mut stdout = std::io::stdout();
    let invs = get_host_inventories(cmd.clone()).await?;
    sp.finish_and_clear();
    execute!(stdout, Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0))
        .map_err(|e| anyhow::anyhow!("Failed to clear terminal: {}", e))?;
    let output = host_inventories_table(invs);
    stdout
        .write_all(output.as_bytes())
        .map_err(|e| anyhow::anyhow!("Failed to write inventory to stdout: {}", e))?;

    let mut ctrlc = std::pin::pin!(tokio::signal::ctrl_c());
    let watch_interval = cmd.watch.unwrap_or(Duration::from_millis(5000));

    loop {
        let invs = tokio::select! {
            res = get_host_inventories(cmd.clone()) => res?,
            res = &mut ctrlc => {
                res?;
                execute!(stdout, Clear(ClearType::Purge),Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0), cursor::Show)
                    .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;
                stdout.flush()
                    .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;
                return Ok(());
            }
        };

        execute!(stdout, Clear(ClearType::Purge), cursor::MoveTo(0, 0))
            .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;

        let output = host_inventories_table(invs);
        stdout
            .write_all(output.as_bytes())
            .map_err(|e| anyhow::anyhow!("Failed to write inventory to stdout: {}", e))?;

        stdout
            .flush()
            .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;

        execute!(
            stdout,
            Clear(ClearType::CurrentLine),
            Clear(ClearType::FromCursorDown),
        )
        .map_err(|e| anyhow::anyhow!("Failed to clear terminal: {}", e))?;

        tokio::select! {
            _ = sleep(watch_interval) => continue,
            res = &mut ctrlc => {
                res?;
                execute!(stdout, Clear(ClearType::Purge),Clear(ClearType::FromCursorUp), cursor::MoveTo(0, 0), cursor::Show)
                    .map_err(|e| anyhow::anyhow!("Failed to execute terminal commands: {}", e))?;
                stdout.flush()
                    .map_err(|e| anyhow::anyhow!("Failed to flush stdout: {}", e))?;
                return Ok(());
            }
        }
    }
}