1use clap::{Args, Parser, Subcommand};
2
3#[derive(Debug, Parser)]
4#[command(
5 name = "agent-sim",
6 version,
7 about = "Stateful firmware simulation runtime CLI"
8)]
9pub struct CliArgs {
10 #[arg(long, global = true, env = "AGENT_SIM_JSON", default_value_t = false)]
12 pub json: bool,
13
14 #[arg(
16 long,
17 global = true,
18 env = "AGENT_SIM_INSTANCE",
19 default_value = "default"
20 )]
21 pub instance: String,
22
23 #[arg(long, global = true, env = "AGENT_SIM_CONFIG")]
25 pub config: Option<String>,
26
27 #[command(subcommand)]
28 pub command: Option<Command>,
29}
30
31#[derive(Debug, Subcommand)]
32pub enum Command {
33 Load(LoadArgs),
34 Info,
35 Signals,
36 Can(CanArgs),
37 Shared(SharedArgs),
38 Reset,
39 Get(GetArgs),
40 Set(SetArgs),
41 Watch(WatchArgs),
42 Run(RunArgs),
43 Close(CloseArgs),
44 Env(EnvArgs),
45 Instance(InstanceArgs),
46 Time(TimeArgs),
47}
48
49#[derive(Debug, Args)]
50pub struct LoadArgs {
51 pub libpath: Option<String>,
52 #[arg(long = "flash")]
53 pub flash: Vec<String>,
54}
55
56#[derive(Debug, Args)]
57pub struct CloseArgs {
58 #[arg(long, default_value_t = false)]
59 pub all: bool,
60 #[arg(long)]
61 pub env: Option<String>,
62}
63
64#[derive(Debug, Args)]
65pub struct EnvArgs {
66 #[command(subcommand)]
67 pub command: EnvCommand,
68}
69
70#[derive(Debug, Subcommand)]
71pub enum EnvCommand {
72 Start {
73 name: String,
74 },
75 Status {
76 name: String,
77 },
78 Reset {
79 name: String,
80 },
81 Time {
82 name: String,
83 #[command(subcommand)]
84 command: TimeCommand,
85 },
86 Can {
87 name: String,
88 #[command(subcommand)]
89 command: EnvCanCommand,
90 },
91}
92
93#[derive(Debug, Args)]
94pub struct InstanceArgs {
95 #[command(subcommand)]
96 pub command: Option<InstanceCommand>,
97}
98
99#[derive(Debug, Args)]
100pub struct CanArgs {
101 #[command(subcommand)]
102 pub command: CanCommand,
103}
104
105#[derive(Debug, Subcommand)]
106pub enum CanCommand {
107 Buses,
108 Attach {
109 bus: String,
110 vcan_iface: String,
111 },
112 Detach {
113 bus: String,
114 },
115 LoadDbc {
116 bus: String,
117 path: String,
118 },
119 Send {
120 bus: String,
121 arb_id: String,
122 data_hex: String,
123 #[arg(long)]
124 flags: Option<u8>,
125 },
126}
127
128#[derive(Debug, Subcommand)]
129pub enum EnvCanCommand {
130 Buses,
131 LoadDbc {
132 bus: String,
133 path: String,
134 },
135 Send {
136 bus: String,
137 arb_id: String,
138 data_hex: String,
139 #[arg(long)]
140 flags: Option<u8>,
141 },
142 Inspect {
143 bus: String,
144 },
145 Schedule {
146 #[command(subcommand)]
147 command: EnvCanScheduleCommand,
148 },
149}
150
151#[derive(Debug, Subcommand)]
152pub enum EnvCanScheduleCommand {
153 Add {
154 bus: String,
155 arb_id: String,
156 data_hex: String,
157 every: String,
158 #[arg(long)]
159 job_id: Option<String>,
160 #[arg(long)]
161 flags: Option<u8>,
162 },
163 Update {
164 job_id: String,
165 arb_id: String,
166 data_hex: String,
167 every: String,
168 #[arg(long)]
169 flags: Option<u8>,
170 },
171 Remove {
172 job_id: String,
173 },
174 Stop {
175 job_id: String,
176 },
177 Start {
178 job_id: String,
179 },
180 List {
181 bus: Option<String>,
182 },
183}
184
185#[derive(Debug, Args)]
186pub struct SharedArgs {
187 #[command(subcommand)]
188 pub command: SharedCommand,
189}
190
191#[derive(Debug, Subcommand)]
192pub enum SharedCommand {
193 List,
194 Get { channel: String },
195}
196
197#[derive(Debug, Subcommand)]
198pub enum InstanceCommand {
199 List,
200}
201
202#[derive(Debug, Args)]
203pub struct TimeArgs {
204 #[command(subcommand)]
205 pub command: TimeCommand,
206}
207
208#[derive(Debug, Subcommand)]
209pub enum TimeCommand {
210 Start,
211 Pause,
212 Step { duration: String },
213 Speed { multiplier: Option<f64> },
214 Status,
215}
216
217#[derive(Debug, Args)]
218pub struct GetArgs {
219 #[arg(required = true)]
220 pub selectors: Vec<String>,
221}
222
223#[derive(Debug, Args)]
224pub struct SetArgs {
225 #[arg(required = true)]
227 pub entries: Vec<String>,
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn env_can_schedule_start_parses() {
236 let args = CliArgs::try_parse_from([
237 "agent-sim",
238 "env",
239 "can",
240 "demo-env",
241 "schedule",
242 "start",
243 "job-1",
244 ])
245 .expect("schedule start command should parse");
246
247 let Some(Command::Env(EnvArgs {
248 command:
249 EnvCommand::Can {
250 name,
251 command:
252 EnvCanCommand::Schedule {
253 command: EnvCanScheduleCommand::Start { job_id },
254 },
255 },
256 })) = args.command
257 else {
258 panic!("expected env can schedule start command");
259 };
260
261 assert_eq!(name, "demo-env");
262 assert_eq!(job_id, "job-1");
263 }
264}
265
266#[derive(Debug, Args)]
267pub struct WatchArgs {
268 pub selector: String,
269 #[arg(default_value_t = 200)]
270 pub interval_ms: u64,
271 #[arg(long)]
272 pub samples: Option<u32>,
273}
274
275#[derive(Debug, Args)]
276pub struct RunArgs {
277 pub recipe_name: String,
278 #[arg(long)]
279 pub dry_run: bool,
280}