Skip to main content

agent_sim/cli/
args.rs

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    /// JSON output mode
11    #[arg(long, global = true, env = "AGENT_SIM_JSON", default_value_t = false)]
12    pub json: bool,
13
14    /// Named instance
15    #[arg(
16        long,
17        global = true,
18        env = "AGENT_SIM_INSTANCE",
19        default_value = "default"
20    )]
21    pub instance: String,
22
23    /// Config file path
24    #[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    /// Either "<signal> <value>" or repeated "<signal>=<value>" pairs
226    #[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}