ironflow_cli/commands/
agent_role.rs1use std::slice::from_ref;
4
5use anyhow::{Result, bail};
6use clap::{Args, Subcommand};
7use comfy_table::{ContentArrangement, Table};
8use ironflow_sdk::IronflowClient;
9use ironflow_sdk::types::{AgentRoleResponse, UpdateAgentRoleCostCapRequest};
10
11use crate::output;
12
13#[derive(Debug, Args)]
15pub struct AgentRoleArgs {
16 #[command(subcommand)]
18 pub command: AgentRoleCommands,
19}
20
21#[derive(Debug, Subcommand)]
23pub enum AgentRoleCommands {
24 List {
26 #[arg(long)]
28 page: Option<u32>,
29 #[arg(long)]
31 per_page: Option<u32>,
32 },
33 Show {
35 name: String,
37 },
38 SetCap {
41 name: String,
43 #[arg(long)]
45 usd: f64,
46 },
47 ClearCap {
49 name: String,
51 },
52}
53
54fn usd(value: Option<f64>) -> String {
56 value.map_or_else(|| "-".to_string(), |v| format!("${v:.4}"))
57}
58
59fn roles_table(items: &[AgentRoleResponse]) -> Table {
60 let mut table = Table::new();
61 table.set_content_arrangement(ContentArrangement::Dynamic);
62 table.set_header(vec![
63 "NAME",
64 "PERIOD",
65 "SPENT",
66 "RESERVED",
67 "CAP",
68 "REMAINING",
69 "RESETS AT",
70 ]);
71 for role in items {
72 table.add_row(vec![
73 role.name.clone(),
74 role.period.clone(),
75 usd(Some(role.spent_usd)),
76 usd(Some(role.reserved_usd)),
77 usd(role.cap_usd),
78 usd(role.remaining_usd),
79 role.resets_at.to_string(),
80 ]);
81 }
82 table
83}
84
85fn cap_request(usd: Option<f64>) -> Result<UpdateAgentRoleCostCapRequest> {
87 if let Some(cap) = usd
88 && !(cap.is_finite() && cap >= 0.0)
89 {
90 bail!("--usd must be a number at least 0, got {cap}");
91 }
92 Ok(UpdateAgentRoleCostCapRequest {
93 operator_cap_usd: usd,
94 })
95}
96
97pub async fn execute(client: &IronflowClient, args: &AgentRoleArgs, json_mode: bool) -> Result<()> {
103 match &args.command {
104 AgentRoleCommands::List { page, per_page } => {
105 let response = client.list_agent_roles(*page, *per_page).await?;
106 output::print_output(json_mode, &response, || roles_table(&response.data))
107 }
108 AgentRoleCommands::Show { name } => {
109 let response = client.get_agent_role(name).await?;
110 output::print_output(json_mode, &response, || {
111 roles_table(from_ref(&response.data))
112 })
113 }
114 AgentRoleCommands::SetCap { name, usd } => {
115 let request = cap_request(Some(*usd))?;
116 let response = client.set_agent_role_cost_cap(name, &request).await?;
117 output::print_output(json_mode, &response, || {
118 roles_table(from_ref(&response.data))
119 })
120 }
121 AgentRoleCommands::ClearCap { name } => {
122 let request = cap_request(None)?;
123 let response = client.set_agent_role_cost_cap(name, &request).await?;
124 output::print_output(json_mode, &response, || {
125 roles_table(from_ref(&response.data))
126 })
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn usd_formats_a_value_or_a_dash() {
137 assert_eq!(usd(Some(1.5)), "$1.5000");
138 assert_eq!(usd(None), "-");
139 }
140
141 #[test]
142 fn cap_request_refuses_a_negative_or_nan_cap() {
143 assert!(cap_request(Some(-1.0)).is_err());
144 assert!(cap_request(Some(f64::NAN)).is_err());
145 assert_eq!(cap_request(Some(2.5)).unwrap().operator_cap_usd, Some(2.5));
146 assert_eq!(cap_request(None).unwrap().operator_cap_usd, None);
147 }
148}