Skip to main content

agent_can/cli/
args.rs

1use crate::protocol::{
2    ADAPTERS_LIST_SUMMARY, CLI_ABOUT, CONNECT_SUMMARY, DISCONNECT_SUMMARY, MESSAGE_LIST_SUMMARY,
3    MESSAGE_READ_SUMMARY, MESSAGE_SEND_SUMMARY, MESSAGE_STOP_SUMMARY, SCHEMA_SUMMARY,
4    SELECTOR_RULES, STATUS_SUMMARY, TRACE_START_SUMMARY, TRACE_STOP_SUMMARY,
5};
6use clap::{ArgAction, Args, Parser, Subcommand};
7
8#[derive(Debug, Parser)]
9#[command(
10    name = "agent-can",
11    version,
12    about = CLI_ABOUT
13)]
14pub struct CliArgs {
15    #[arg(long, global = true, env = "AGENT_CAN_JSON", default_value_t = false)]
16    pub json: bool,
17
18    #[arg(long, hide = true, default_value_t = false)]
19    pub mcp: bool,
20
21    #[command(subcommand)]
22    pub command: Option<Command>,
23}
24
25#[derive(Debug, Subcommand)]
26pub enum Command {
27    #[command(about = ADAPTERS_LIST_SUMMARY)]
28    Adapters(AdaptersArgs),
29    #[command(about = CONNECT_SUMMARY)]
30    Connect(ConnectArgs),
31    #[command(about = DISCONNECT_SUMMARY)]
32    Disconnect,
33    #[command(about = STATUS_SUMMARY)]
34    Status,
35    #[command(about = SCHEMA_SUMMARY, after_help = SELECTOR_RULES)]
36    Schema(SchemaArgs),
37    Message(MessageArgs),
38    Trace(TraceArgs),
39}
40
41#[derive(Debug, Args)]
42pub struct AdaptersArgs {
43    #[command(subcommand)]
44    pub command: AdaptersCommand,
45}
46
47#[derive(Debug, Subcommand)]
48pub enum AdaptersCommand {
49    List,
50}
51
52#[derive(Debug, Args)]
53pub struct ConnectArgs {
54    #[arg(long)]
55    pub adapter: String,
56    #[arg(long)]
57    pub bitrate: u32,
58    #[arg(long)]
59    pub bitrate_data: Option<u32>,
60    #[arg(long, default_value_t = false)]
61    pub fd: bool,
62    #[arg(long = "dbc", value_name = "alias=path", action = ArgAction::Append)]
63    pub dbcs: Vec<String>,
64}
65
66#[derive(Debug, Args)]
67pub struct SchemaArgs {
68    #[arg(long)]
69    pub filter: Option<String>,
70}
71
72#[derive(Debug, Args)]
73pub struct MessageArgs {
74    #[command(subcommand)]
75    pub command: MessageCommand,
76}
77
78#[derive(Debug, Subcommand)]
79pub enum MessageCommand {
80    #[command(about = MESSAGE_LIST_SUMMARY, after_help = SELECTOR_RULES)]
81    List(MessageListArgs),
82    #[command(about = MESSAGE_READ_SUMMARY, after_help = SELECTOR_RULES)]
83    Read(MessageReadArgs),
84    #[command(about = MESSAGE_SEND_SUMMARY, after_help = SELECTOR_RULES)]
85    Send(MessageSendArgs),
86    #[command(about = MESSAGE_STOP_SUMMARY, after_help = SELECTOR_RULES)]
87    Stop(MessageStopArgs),
88}
89
90#[derive(Debug, Args)]
91pub struct MessageListArgs {
92    #[arg(long)]
93    pub filter: Option<String>,
94    #[arg(long, default_value_t = false)]
95    pub allow_raw: bool,
96    #[arg(long, default_value_t = false)]
97    pub include_tx: bool,
98}
99
100#[derive(Debug, Args)]
101pub struct MessageReadArgs {
102    #[arg(long)]
103    pub select: String,
104    #[arg(long)]
105    pub count: Option<usize>,
106    #[arg(long, default_value_t = false)]
107    pub include_tx: bool,
108}
109
110#[derive(Debug, Args)]
111pub struct MessageSendArgs {
112    #[arg(long)]
113    pub target: String,
114    #[arg(long, value_name = "hex-or-json")]
115    pub data: String,
116    #[arg(long)]
117    pub periodicity_ms: Option<u64>,
118}
119
120#[derive(Debug, Args)]
121pub struct MessageStopArgs {
122    #[arg(long)]
123    pub target: String,
124}
125
126#[derive(Debug, Args)]
127pub struct TraceArgs {
128    #[command(subcommand)]
129    pub command: TraceCommand,
130}
131
132#[derive(Debug, Subcommand)]
133pub enum TraceCommand {
134    #[command(about = TRACE_START_SUMMARY)]
135    Start(TraceStartArgs),
136    #[command(about = TRACE_STOP_SUMMARY)]
137    Stop,
138}
139
140#[derive(Debug, Args)]
141pub struct TraceStartArgs {
142    #[arg(long)]
143    pub path: String,
144}
145
146#[cfg(test)]
147mod tests {
148    use super::{CliArgs, Command, MessageCommand};
149    use clap::Parser;
150
151    #[test]
152    fn parses_grouped_connect_command() {
153        let args = CliArgs::parse_from([
154            "agent-can",
155            "connect",
156            "--adapter",
157            "usb1",
158            "--bitrate",
159            "500000",
160            "--dbc",
161            "vehicle=./vehicle.dbc",
162        ]);
163        assert!(matches!(args.command, Some(Command::Connect(_))));
164    }
165
166    #[test]
167    fn parses_grouped_message_command() {
168        let args = CliArgs::parse_from([
169            "agent-can",
170            "message",
171            "send",
172            "--target",
173            "0x123",
174            "--data",
175            "DEADBEEF",
176        ]);
177        assert!(matches!(
178            args.command,
179            Some(Command::Message(super::MessageArgs {
180                command: MessageCommand::Send(_)
181            }))
182        ));
183    }
184}