1use clap::{Parser, Subcommand};
2use std::path::PathBuf;
3
4#[derive(Parser, Debug)]
5#[command(
6 name = "rust-analyzer-cli",
7 author,
8 version,
9 about = "Compiler-accurate Rust codebase navigation powered by rust-analyzer LSP"
10)]
11pub struct Cli {
12 #[arg(short, long, env = "RUST_ANALYZER_CLI_PORT", default_value_t = 60094)]
14 pub port: u16,
15
16 #[arg(short, long, global = true)]
18 pub json: bool,
19
20 #[command(subcommand)]
21 pub command: Commands,
22}
23
24#[derive(Subcommand, Debug)]
25pub enum Commands {
26 Daemon {
28 #[arg(short, long, default_value = ".")]
30 workspace: PathBuf,
31 },
32 Status,
34 Refresh,
36 Symbol {
38 name: String,
40 #[arg(default_value = "any")]
42 kind: String,
43 #[arg(long)]
45 exact: bool,
46 },
47 Outline {
49 #[arg(short, long)]
51 file: Option<PathBuf>,
52 #[arg(long)]
54 file_list: Option<String>,
55 #[arg(short, long)]
57 output: Option<PathBuf>,
58 },
59 Definition {
61 #[arg(short, long)]
63 file: PathBuf,
64 #[arg(short, long)]
66 line: u32,
67 #[arg(short, long)]
69 col: u32,
70 },
71 Cursor {
73 #[arg(short, long)]
75 file: PathBuf,
76 #[arg(short, long)]
78 line: u32,
79 #[arg(short, long)]
81 col: u32,
82 #[arg(short, long, default_value = "incoming")]
84 mode: String,
85 #[arg(short, long, default_value_t = 1)]
87 depth: u32,
88 },
89 TypeHierarchy {
91 #[arg(short, long)]
93 file: PathBuf,
94 #[arg(short, long)]
96 line: u32,
97 #[arg(short, long)]
99 col: u32,
100 #[arg(short, long, default_value = "supertypes")]
102 mode: String,
103 #[arg(short, long, default_value_t = 1)]
105 depth: u32,
106 },
107 Check {
109 #[arg(short, long)]
111 target: Option<String>,
112 },
113 InitSkill {
115 #[arg(short, long, default_value = ".")]
117 workspace: PathBuf,
118 #[arg(short, long)]
120 dir: Option<PathBuf>,
121 },
122}
123
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_cli_symbol_parsing() {
131 let args = vec!["rust-analyzer-cli", "symbol", "LspClient", "struct", "--exact"];
132 let cli = Cli::try_parse_from(args).unwrap();
133 assert_eq!(cli.port, 60094);
134 assert!(!cli.json);
135 match cli.command {
136 Commands::Symbol { name, kind, exact } => {
137 assert_eq!(name, "LspClient");
138 assert_eq!(kind, "struct");
139 assert!(exact);
140 }
141 _ => panic!("Expected Symbol command"),
142 }
143 }
144
145 #[test]
146 fn test_cli_outline_parsing() {
147 let args = vec!["rust-analyzer-cli", "--json", "outline", "-f", "src/main.rs"];
148 let cli = Cli::try_parse_from(args).unwrap();
149 assert!(cli.json);
150 match cli.command {
151 Commands::Outline { file, file_list, output } => {
152 assert_eq!(file, Some(PathBuf::from("src/main.rs")));
153 assert!(file_list.is_none());
154 assert!(output.is_none());
155 }
156 _ => panic!("Expected Outline command"),
157 }
158 }
159
160 #[test]
161 fn test_cli_definition_parsing() {
162 let args = vec!["rust-analyzer-cli", "definition", "-f", "src/lib.rs", "-l", "10", "-c", "5"];
163 let cli = Cli::try_parse_from(args).unwrap();
164 match cli.command {
165 Commands::Definition { file, line, col } => {
166 assert_eq!(file, PathBuf::from("src/lib.rs"));
167 assert_eq!(line, 10);
168 assert_eq!(col, 5);
169 }
170 _ => panic!("Expected Definition command"),
171 }
172 }
173
174 #[test]
175 fn test_cli_init_skill_parsing() {
176 let args = vec!["rust-analyzer-cli", "init-skill", "-w", "/my/proj", "-d", "custom/SKILL.md"];
177 let cli = Cli::try_parse_from(args).unwrap();
178 match cli.command {
179 Commands::InitSkill { workspace, dir } => {
180 assert_eq!(workspace, PathBuf::from("/my/proj"));
181 assert_eq!(dir, Some(PathBuf::from("custom/SKILL.md")));
182 }
183 _ => panic!("Expected InitSkill command"),
184 }
185 }
186}
187
188
189