kratactl/cli/device/
list.rs1use 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}