palladium_cli/commands/
actor.rs1use clap::{Args, Subcommand};
2use schemars::JsonSchema;
3use serde::Serialize;
4use serde_json::{json, Value};
5
6use crate::client::ControlPlaneClient;
7use crate::client::Endpoint;
8use crate::output;
9use crate::CliResult;
10
11#[derive(Subcommand, Debug, Serialize, JsonSchema)]
12#[serde(rename_all = "kebab-case")]
13pub enum ActorCommand {
14 List(ActorListArgs),
16 Info(ActorInfoArgs),
18 Stop(ActorStopArgs),
20 Restart(ActorRestartArgs),
22 Kill(ActorKillArgs),
24 #[command(subcommand)]
26 Binding(ActorBindingCommand),
27 Spawn(ActorSpawnArgs),
29}
30
31#[derive(Subcommand, Debug, Serialize, JsonSchema)]
32#[serde(rename_all = "kebab-case")]
33pub enum ActorBindingCommand {
34 Get(ActorBindingGetArgs),
36}
37
38#[derive(Args, Debug, Serialize, JsonSchema)]
39#[serde(rename_all = "kebab-case")]
40pub struct ActorListArgs {
41 #[arg(long)]
43 pub path: Option<String>,
44 #[arg(long)]
46 pub state: Option<String>,
47 #[arg(long)]
49 pub json: bool,
50}
51
52#[derive(Args, Debug, Serialize, JsonSchema)]
53#[serde(rename_all = "kebab-case")]
54pub struct ActorInfoArgs {
55 pub path: String,
57 #[arg(long)]
59 pub json: bool,
60}
61
62#[derive(Args, Debug, Serialize, JsonSchema)]
63#[serde(rename_all = "kebab-case")]
64pub struct ActorStopArgs {
65 pub path: String,
67 #[arg(long)]
69 pub force: bool,
70}
71
72#[derive(Args, Debug, Serialize, JsonSchema)]
73#[serde(rename_all = "kebab-case")]
74pub struct ActorRestartArgs {
75 pub path: String,
77 #[arg(long)]
79 pub json: bool,
80}
81
82#[derive(Args, Debug, Serialize, JsonSchema)]
83#[serde(rename_all = "kebab-case")]
84pub struct ActorKillArgs {
85 pub path: String,
87 #[arg(long)]
89 pub json: bool,
90}
91
92#[derive(Args, Debug, Serialize, JsonSchema)]
93#[serde(rename_all = "kebab-case")]
94pub struct ActorBindingGetArgs {
95 pub path: String,
97 #[arg(long)]
99 pub json: bool,
100}
101
102#[derive(Args, Debug, Serialize, JsonSchema)]
103#[serde(rename_all = "kebab-case")]
104pub struct ActorSpawnArgs {
105 pub type_name: String,
107 #[arg(long)]
109 pub path: String,
110 #[arg(long)]
112 pub plugin: Option<String>,
113 #[arg(long)]
115 pub config: Option<String>,
116 #[arg(long)]
118 pub json: bool,
119}
120
121pub fn run(cmd: ActorCommand, endpoint: &Endpoint) -> CliResult {
122 let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
123 match cmd {
124 ActorCommand::List(args) => {
125 let mut params = serde_json::Map::new();
126 if let Some(prefix) = &args.path {
127 params.insert("path_prefix".into(), Value::String(prefix.clone()));
128 }
129 if let Some(state) = &args.state {
130 params.insert("state".into(), Value::String(state.clone()));
131 }
132 let result = client.call("actor.list", Value::Object(params))?;
133 let actors = result.as_array().cloned().unwrap_or_default();
134 if args.json {
135 println!("{}", serde_json::to_string_pretty(&Value::Array(actors))?);
136 } else {
137 print!("{}", output::format_actor_table(&actors));
138 }
139 }
140 ActorCommand::Info(args) => {
141 let result = client.call("actor.info", json!({"path": args.path}))?;
142 if args.json {
143 println!("{}", serde_json::to_string_pretty(&result)?);
144 } else {
145 print!("{}", output::format_actor_info(&result));
146 }
147 }
148 ActorCommand::Stop(args) => {
149 client.call("actor.stop", json!({"path": args.path}))?;
151 println!("Actor {} stopped.", args.path);
152 }
153 ActorCommand::Restart(args) => {
154 let result = client.call("actor.restart", json!({"path": args.path}))?;
155 if args.json {
156 println!("{}", serde_json::to_string_pretty(&result)?);
157 } else {
158 println!("Actor {} restart requested.", args.path);
159 }
160 }
161 ActorCommand::Kill(args) => {
162 let result = client.call("actor.kill", json!({"path": args.path}))?;
163 if args.json {
164 println!("{}", serde_json::to_string_pretty(&result)?);
165 } else {
166 println!("Actor {} killed.", args.path);
167 }
168 }
169 ActorCommand::Binding(cmd) => match cmd {
170 ActorBindingCommand::Get(args) => {
171 let result = client.call("actor.binding.get", json!({"path": args.path}))?;
172 if args.json {
173 println!("{}", serde_json::to_string_pretty(&result)?);
174 } else {
175 let group = result
176 .get("group")
177 .and_then(|v| v.as_str())
178 .unwrap_or("unknown");
179 println!("Actor {} is bound to consensus group {}.", args.path, group);
180 }
181 }
182 },
183 ActorCommand::Spawn(args) => {
184 let mut params = serde_json::Map::new();
185 params.insert("path".into(), Value::String(args.path.clone()));
186 params.insert("type_name".into(), Value::String(args.type_name.clone()));
187 if let Some(plugin) = &args.plugin {
188 params.insert("plugin".into(), Value::String(plugin.clone()));
189 }
190 if let Some(raw) = &args.config {
191 let parsed: Value = serde_json::from_str(raw)?;
192 params.insert("config".into(), parsed);
193 }
194 let result = client.call("actor.spawn", Value::Object(params))?;
195 if args.json {
196 println!("{}", serde_json::to_string_pretty(&result)?);
197 } else {
198 let path = result
199 .get("path")
200 .and_then(|v| v.as_str())
201 .unwrap_or(&args.path);
202 println!("Actor spawned at {}.", path);
203 }
204 }
205 }
206 Ok(())
207}