mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-06 22:51:31 +00:00
feature(kratactl): rework cli to use subcommands (#268)
This commit is contained in:
128
crates/ctl/src/cli/device/list.rs
Normal file
128
crates/ctl/src/cli/device/list.rs
Normal file
@ -0,0 +1,128 @@
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use comfy_table::{presets::UTF8_FULL_CONDENSED, Cell, Color, Table};
|
||||
use krata::{
|
||||
events::EventStream,
|
||||
v1::control::{control_service_client::ControlServiceClient, DeviceInfo, ListDevicesRequest},
|
||||
};
|
||||
|
||||
use serde_json::Value;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::format::{kv2line, proto2dynamic, proto2kv};
|
||||
|
||||
#[derive(ValueEnum, Clone, Debug, PartialEq, Eq)]
|
||||
enum DeviceListFormat {
|
||||
Table,
|
||||
Json,
|
||||
JsonPretty,
|
||||
Jsonl,
|
||||
Yaml,
|
||||
KeyValue,
|
||||
Simple,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "List the devices on the isolation engine")]
|
||||
pub struct DeviceListCommand {
|
||||
#[arg(short, long, default_value = "table", help = "Output format")]
|
||||
format: DeviceListFormat,
|
||||
}
|
||||
|
||||
impl DeviceListCommand {
|
||||
pub async fn run(
|
||||
self,
|
||||
mut client: ControlServiceClient<Channel>,
|
||||
_events: EventStream,
|
||||
) -> Result<()> {
|
||||
let reply = client
|
||||
.list_devices(ListDevicesRequest {})
|
||||
.await?
|
||||
.into_inner();
|
||||
let mut devices = reply.devices;
|
||||
|
||||
devices.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
match self.format {
|
||||
DeviceListFormat::Table => {
|
||||
self.print_devices_table(devices)?;
|
||||
}
|
||||
|
||||
DeviceListFormat::Simple => {
|
||||
for device in devices {
|
||||
println!("{}\t{}\t{}", device.name, device.claimed, device.owner);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceListFormat::Json | DeviceListFormat::JsonPretty | DeviceListFormat::Yaml => {
|
||||
let mut values = Vec::new();
|
||||
for device in devices {
|
||||
let message = proto2dynamic(device)?;
|
||||
values.push(serde_json::to_value(message)?);
|
||||
}
|
||||
let value = Value::Array(values);
|
||||
let encoded = if self.format == DeviceListFormat::JsonPretty {
|
||||
serde_json::to_string_pretty(&value)?
|
||||
} else if self.format == DeviceListFormat::Yaml {
|
||||
serde_yaml::to_string(&value)?
|
||||
} else {
|
||||
serde_json::to_string(&value)?
|
||||
};
|
||||
println!("{}", encoded.trim());
|
||||
}
|
||||
|
||||
DeviceListFormat::Jsonl => {
|
||||
for device in devices {
|
||||
let message = proto2dynamic(device)?;
|
||||
println!("{}", serde_json::to_string(&message)?);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceListFormat::KeyValue => {
|
||||
self.print_key_value(devices)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_devices_table(&self, devices: Vec<DeviceInfo>) -> Result<()> {
|
||||
let mut table = Table::new();
|
||||
table.load_preset(UTF8_FULL_CONDENSED);
|
||||
table.set_content_arrangement(comfy_table::ContentArrangement::Dynamic);
|
||||
table.set_header(vec!["name", "status", "owner"]);
|
||||
for device in devices {
|
||||
let status_text = if device.claimed {
|
||||
"claimed"
|
||||
} else {
|
||||
"available"
|
||||
};
|
||||
|
||||
let status_color = if device.claimed {
|
||||
Color::Blue
|
||||
} else {
|
||||
Color::Green
|
||||
};
|
||||
|
||||
table.add_row(vec![
|
||||
Cell::new(device.name),
|
||||
Cell::new(status_text).fg(status_color),
|
||||
Cell::new(device.owner),
|
||||
]);
|
||||
}
|
||||
if table.is_empty() {
|
||||
println!("no devices configured");
|
||||
} else {
|
||||
println!("{}", table);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_key_value(&self, devices: Vec<DeviceInfo>) -> Result<()> {
|
||||
for device in devices {
|
||||
let kvs = proto2kv(device)?;
|
||||
println!("{}", kv2line(kvs));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
44
crates/ctl/src/cli/device/mod.rs
Normal file
44
crates/ctl/src/cli/device/mod.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use krata::events::EventStream;
|
||||
use krata::v1::control::control_service_client::ControlServiceClient;
|
||||
|
||||
use crate::cli::device::list::DeviceListCommand;
|
||||
|
||||
pub mod list;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(about = "Manage the devices on the isolation engine")]
|
||||
pub struct DeviceCommand {
|
||||
#[command(subcommand)]
|
||||
subcommand: DeviceCommands,
|
||||
}
|
||||
|
||||
impl DeviceCommand {
|
||||
pub async fn run(
|
||||
self,
|
||||
client: ControlServiceClient<Channel>,
|
||||
events: EventStream,
|
||||
) -> Result<()> {
|
||||
self.subcommand.run(client, events).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum DeviceCommands {
|
||||
List(DeviceListCommand),
|
||||
}
|
||||
|
||||
impl DeviceCommands {
|
||||
pub async fn run(
|
||||
self,
|
||||
client: ControlServiceClient<Channel>,
|
||||
events: EventStream,
|
||||
) -> Result<()> {
|
||||
match self {
|
||||
DeviceCommands::List(list) => list.run(client, events).await,
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user