kratactl/cli/device/
list.rs

1use anyhow::Result;
2use clap::{Parser, ValueEnum};
3use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Color, Table};
4use krata::{
5    events::EventStream,
6    v1::control::{control_service_client::ControlServiceClient, DeviceInfo, ListDevicesRequest},
7};
8
9use serde_json::Value;
10use tonic::transport::Channel;
11
12use crate::format::{kv2line, proto2dynamic, proto2kv};
13
14#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
15enum DeviceListFormat {
16    Table,
17    Json,
18    JsonPretty,
19    Jsonl,
20    Yaml,
21    KeyValue,
22    Simple,
23}
24
25#[derive(Parser)]
26#[command(about = "List device information")]
27pub struct DeviceListCommand {
28    #[arg(short, long, default_value = "table", help = "Output format")]
29    format: DeviceListFormat,
30}
31
32impl DeviceListCommand {
33    pub async fn run(
34        self,
35        mut client: ControlServiceClient<Channel>,
36        _events: EventStream,
37    ) -> Result<()> {
38        let reply = client
39            .list_devices(ListDevicesRequest {})
40            .await?
41            .into_inner();
42        let mut devices = reply.devices;
43
44        devices.sort_by(|a, b| a.name.cmp(&b.name));
45
46        match self.format {
47            DeviceListFormat::Table => {
48                self.print_devices_table(devices)?;
49            }
50
51            DeviceListFormat::Simple => {
52                for device in devices {
53                    println!("{}\t{}\t{}", device.name, device.claimed, device.owner);
54                }
55            }
56
57            DeviceListFormat::Json | DeviceListFormat::JsonPretty | DeviceListFormat::Yaml => {
58                let mut values = Vec::new();
59                for device in devices {
60                    let message = proto2dynamic(device)?;
61                    values.push(serde_json::to_value(message)?);
62                }
63                let value = Value::Array(values);
64                let encoded = if self.format == DeviceListFormat::JsonPretty {
65                    serde_json::to_string_pretty(&value)?
66                } else if self.format == DeviceListFormat::Yaml {
67                    serde_yaml::to_string(&value)?
68                } else {
69                    serde_json::to_string(&value)?
70                };
71                println!("{}", encoded.trim());
72            }
73
74            DeviceListFormat::Jsonl => {
75                for device in devices {
76                    let message = proto2dynamic(device)?;
77                    println!("{}", serde_json::to_string(&message)?);
78                }
79            }
80
81            DeviceListFormat::KeyValue => {
82                self.print_key_value(devices)?;
83            }
84        }
85
86        Ok(())
87    }
88
89    fn print_devices_table(&self, devices: Vec<DeviceInfo>) -> Result<()> {
90        let mut table = Table::new();
91        table.load_preset(UTF8_FULL_CONDENSED);
92        table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
93        table.set_header(vec!["name", "status", "owner"]);
94        for device in devices {
95            let status_text = if device.claimed {
96                "claimed"
97            } else {
98                "available"
99            };
100
101            let status_color = if device.claimed {
102                Color::Blue
103            } else {
104                Color::Green
105            };
106
107            table.add_row(vec![
108                Cell::new(device.name),
109                Cell::new(status_text).fg(status_color),
110                Cell::new(device.owner),
111            ]);
112        }
113        if table.is_empty() {
114            println!("no devices configured");
115        } else {
116            println!("{}", table);
117        }
118        Ok(())
119    }
120
121    fn print_key_value(&self, devices: Vec<DeviceInfo>) -> Result<()> {
122        for device in devices {
123            let kvs = proto2kv(device)?;
124            println!("{}", kv2line(kvs));
125        }
126        Ok(())
127    }
128}