Skip to content

Commit aefcb77

Browse files
authored
Changed all impls to use string slices for better perf (#111)
1 parent fa8ffcc commit aefcb77

File tree

6 files changed

+92
-79
lines changed

6 files changed

+92
-79
lines changed

src/bundler.rs

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::path::Path;
44
/// A simple wrapper around the `bundle` command.
55
pub struct Bundler {
66
pub working_dir: String,
7-
envs: Vec<(String, String)>,
87
command_executor: Box<dyn CommandExecutor>,
98
}
109

@@ -14,14 +13,9 @@ impl Bundler {
1413
/// # Arguments
1514
/// * `working_dir` - The working directory where `bundle` commands should be executed.
1615
/// * `command_executor` - An executor for `bundle` commands.
17-
pub fn new(
18-
working_dir: String,
19-
envs: Vec<(String, String)>,
20-
command_executor: Box<dyn CommandExecutor>,
21-
) -> Self {
16+
pub fn new(working_dir: String, command_executor: Box<dyn CommandExecutor>) -> Self {
2217
Bundler {
2318
working_dir,
24-
envs,
2519
command_executor,
2620
}
2721
}
@@ -33,31 +27,36 @@ impl Bundler {
3327
///
3428
/// # Returns
3529
/// A `Result` containing the version string if successful, or an error message.
36-
pub fn installed_gem_version(&self, name: &str) -> Result<String, String> {
37-
let args = vec!["--version".into(), name.into()];
38-
39-
self.execute_bundle_command("info".into(), args)
30+
pub fn installed_gem_version(
31+
&self,
32+
name: &str,
33+
envs: &[(&str, &str)],
34+
) -> Result<String, String> {
35+
let args = &["--version", name];
36+
37+
self.execute_bundle_command("info", args, envs)
4038
}
4139

42-
fn execute_bundle_command(&self, cmd: String, args: Vec<String>) -> Result<String, String> {
40+
fn execute_bundle_command(
41+
&self,
42+
cmd: &str,
43+
args: &[&str],
44+
envs: &[(&str, &str)],
45+
) -> Result<String, String> {
4346
let bundle_gemfile_path = Path::new(&self.working_dir).join("Gemfile");
4447
let bundle_gemfile = bundle_gemfile_path
4548
.to_str()
4649
.ok_or_else(|| "Invalid path to Gemfile".to_string())?;
4750

48-
let full_args: Vec<String> = std::iter::once(cmd).chain(args).collect();
49-
let command_envs: Vec<(String, String)> = self
50-
.envs
51+
let full_args: Vec<&str> = std::iter::once(cmd).chain(args.iter().copied()).collect();
52+
let command_envs: Vec<(&str, &str)> = envs
5153
.iter()
5254
.cloned()
53-
.chain(std::iter::once((
54-
"BUNDLE_GEMFILE".to_string(),
55-
bundle_gemfile.to_string(),
56-
)))
55+
.chain(std::iter::once(("BUNDLE_GEMFILE", bundle_gemfile)))
5756
.collect();
5857

5958
self.command_executor
60-
.execute("bundle", full_args, command_envs)
59+
.execute("bundle", &full_args, &command_envs)
6160
.and_then(|output| match output.status {
6261
Some(0) => Ok(String::from_utf8_lossy(&output.stdout).to_string()),
6362
Some(status) => {
@@ -128,8 +127,8 @@ mod tests {
128127
fn execute(
129128
&self,
130129
command_name: &str,
131-
args: Vec<String>,
132-
envs: Vec<(String, String)>,
130+
args: &[&str],
131+
envs: &[(&str, &str)],
133132
) -> Result<Output, String> {
134133
let mut config = self.config.borrow_mut();
135134

@@ -140,6 +139,10 @@ mod tests {
140139
assert_eq!(&args, expected_args, "Mock: Args mismatch");
141140
}
142141
if let Some(expected_envs) = &config.expected_envs {
142+
let envs: Vec<(String, String)> = envs
143+
.iter()
144+
.map(|(k, v)| (k.to_string(), v.to_string()))
145+
.collect();
143146
assert_eq!(&envs, expected_envs, "Mock: Env mismatch");
144147
}
145148

@@ -172,9 +175,9 @@ mod tests {
172175
#[test]
173176
fn test_installed_gem_version_success() {
174177
let mock_executor = create_mock_executor_for_success("8.0.0", "test_dir", "rails");
175-
let bundler = Bundler::new("test_dir".into(), vec![], Box::new(mock_executor));
178+
let bundler = Bundler::new("test_dir".into(), Box::new(mock_executor));
176179
let version = bundler
177-
.installed_gem_version("rails")
180+
.installed_gem_version("rails", &[])
178181
.expect("Expected successful version");
179182
assert_eq!(version, "8.0.0", "Installed gem version should match");
180183
}
@@ -197,8 +200,8 @@ mod tests {
197200
}),
198201
);
199202

200-
let bundler = Bundler::new("test_dir".into(), vec![], Box::new(mock_executor));
201-
let result = bundler.installed_gem_version(gem_name);
203+
let bundler = Bundler::new("test_dir".into(), Box::new(mock_executor));
204+
let result = bundler.installed_gem_version(gem_name, &[]);
202205

203206
assert!(
204207
result.is_err(),
@@ -229,8 +232,8 @@ mod tests {
229232
Err(specific_error_msg.to_string()),
230233
);
231234

232-
let bundler = Bundler::new("test_dir".into(), vec![], Box::new(mock_executor));
233-
let result = bundler.installed_gem_version(gem_name);
235+
let bundler = Bundler::new("test_dir".into(), Box::new(mock_executor));
236+
let result = bundler.installed_gem_version(gem_name, &[]);
234237

235238
assert!(result.is_err(), "Expected error from executor failure");
236239
assert_eq!(

src/command_executor.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,26 @@ pub trait CommandExecutor {
1919
fn execute(
2020
&self,
2121
cmd: &str,
22-
args: Vec<String>,
23-
envs: Vec<(String, String)>,
22+
args: &[&str],
23+
envs: &[(&str, &str)],
2424
) -> zed::Result<zed::process::Output>;
2525
}
2626

2727
/// An implementation of `CommandExecutor` that executes commands
2828
/// using the `zed_extension_api::Command`.
29+
#[derive(Clone)]
2930
pub struct RealCommandExecutor;
3031

3132
impl CommandExecutor for RealCommandExecutor {
3233
fn execute(
3334
&self,
3435
cmd: &str,
35-
args: Vec<String>,
36-
envs: Vec<(String, String)>,
36+
args: &[&str],
37+
envs: &[(&str, &str)],
3738
) -> zed::Result<zed::process::Output> {
38-
zed::Command::new(cmd).args(args).envs(envs).output()
39+
zed::Command::new(cmd)
40+
.args(args.iter().copied())
41+
.envs(envs.iter().copied())
42+
.output()
3943
}
4044
}

src/gemset.rs

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@ impl Gemset {
1616
}
1717

1818
/// Returns the full path to a gem binary executable.
19-
pub fn gem_bin_path(&self, bin_name: impl Into<String>) -> Result<String, String> {
20-
let bin_name = bin_name.into();
19+
pub fn gem_bin_path(&self, bin_name: &str) -> Result<String, String> {
2120
let path = std::path::Path::new(&self.gem_home)
2221
.join("bin")
23-
.join(&bin_name);
22+
.join(bin_name);
2423

2524
path.to_str()
2625
.map(ToString::to_string)
@@ -35,21 +34,21 @@ impl Gemset {
3534
}
3635

3736
pub fn install_gem(&self, name: &str) -> Result<(), String> {
38-
let args = vec![
39-
"--no-user-install".to_string(),
40-
"--no-format-executable".to_string(),
41-
"--no-document".to_string(),
42-
name.into(),
37+
let args = &[
38+
"--no-user-install",
39+
"--no-format-executable",
40+
"--no-document",
41+
name,
4342
];
4443

45-
self.execute_gem_command("install".into(), args)
44+
self.execute_gem_command("install", args)
4645
.map_err(|e| format!("Failed to install gem '{name}': {e}"))?;
4746

4847
Ok(())
4948
}
5049

5150
pub fn update_gem(&self, name: &str) -> Result<(), String> {
52-
self.execute_gem_command("update".into(), vec![name.into()])
51+
self.execute_gem_command("update", &[name])
5352
.map_err(|e| format!("Failed to update gem '{name}': {e}"))?;
5453
Ok(())
5554
}
@@ -58,8 +57,8 @@ impl Gemset {
5857
let re =
5958
Regex::new(r"^(\S+) \((.+)\)$").map_err(|e| format!("Failed to compile regex: {e}"))?;
6059

61-
let args = vec!["--exact".to_string(), name.into()];
62-
let output_str = self.execute_gem_command("list".into(), args)?;
60+
let args = &["--exact", name];
61+
let output_str = self.execute_gem_command("list", args)?;
6362

6463
for line in output_str.lines() {
6564
let captures = match re.captures(line) {
@@ -78,23 +77,22 @@ impl Gemset {
7877
}
7978

8079
pub fn is_outdated_gem(&self, name: &str) -> Result<bool, String> {
81-
self.execute_gem_command("outdated".into(), vec![])
82-
.map(|output| {
83-
output
84-
.lines()
85-
.any(|line| line.split_whitespace().next().is_some_and(|n| n == name))
86-
})
80+
self.execute_gem_command("outdated", &[]).map(|output| {
81+
output
82+
.lines()
83+
.any(|line| line.split_whitespace().next().is_some_and(|n| n == name))
84+
})
8785
}
8886

89-
fn execute_gem_command(&self, cmd: String, args: Vec<String>) -> Result<String, String> {
90-
let full_args: Vec<String> = std::iter::once(cmd)
91-
.chain(std::iter::once("--norc".to_string()))
92-
.chain(args)
87+
fn execute_gem_command(&self, cmd: &str, args: &[&str]) -> Result<String, String> {
88+
let full_args: Vec<&str> = std::iter::once(cmd)
89+
.chain(std::iter::once("--norc"))
90+
.chain(args.iter().copied())
9391
.collect();
94-
let command_envs = vec![("GEM_HOME".to_string(), self.gem_home.clone())];
92+
let command_envs = &[("GEM_HOME", self.gem_home.as_str())];
9593

9694
self.command_executor
97-
.execute("gem", full_args, command_envs)
95+
.execute("gem", &full_args, command_envs)
9896
.and_then(|output| match output.status {
9997
Some(0) => Ok(String::from_utf8_lossy(&output.stdout).to_string()),
10098
Some(status) => {
@@ -165,8 +163,8 @@ mod tests {
165163
fn execute(
166164
&self,
167165
command_name: &str,
168-
args: Vec<String>,
169-
envs: Vec<(String, String)>,
166+
args: &[&str],
167+
envs: &[(&str, &str)],
170168
) -> Result<Output, String> {
171169
let mut config = self.config.borrow_mut();
172170

@@ -177,6 +175,10 @@ mod tests {
177175
assert_eq!(&args, expected_args, "Mock: Args mismatch");
178176
}
179177
if let Some(expected_envs) = &config.expected_envs {
178+
let envs: Vec<(String, String)> = envs
179+
.iter()
180+
.map(|(k, v)| (k.to_string(), v.to_string()))
181+
.collect();
180182
assert_eq!(&envs, expected_envs, "Mock: Env mismatch");
181183
}
182184

src/language_servers/language_server.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -164,12 +164,14 @@ pub trait LanguageServer {
164164
return self.try_find_on_path_or_extension_gemset(language_server_id, worktree);
165165
}
166166

167-
let bundler = Bundler::new(
168-
worktree.root_path(),
169-
worktree.shell_env(),
170-
Box::new(RealCommandExecutor),
171-
);
172-
match bundler.installed_gem_version(Self::GEM_NAME) {
167+
let bundler = Bundler::new(worktree.root_path(), Box::new(RealCommandExecutor));
168+
let shell_env = worktree.shell_env();
169+
let env_vars: Vec<(&str, &str)> = shell_env
170+
.iter()
171+
.map(|(key, value)| (key.as_str(), value.as_str()))
172+
.collect();
173+
174+
match bundler.installed_gem_version(Self::GEM_NAME, &env_vars) {
173175
Ok(_version) => {
174176
let bundle_path = worktree
175177
.which("bundle")
@@ -183,7 +185,7 @@ pub trait LanguageServer {
183185
.chain(self.get_executable_args(worktree))
184186
.collect(),
185187
),
186-
env: Some(worktree.shell_env()),
188+
env: Some(shell_env),
187189
})
188190
}
189191
Err(_e) => self.try_find_on_path_or_extension_gemset(language_server_id, worktree),

src/language_servers/sorbet.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ impl LanguageServer for Sorbet {
1212
.lsp_binary_settings(Self::SERVER_ID)
1313
.unwrap_or_default();
1414

15-
let default_args = vec![
16-
"tc".to_string(),
17-
"--lsp".to_string(),
18-
"--enable-experimental-lsp-document-highlight".to_string(),
19-
];
20-
2115
// test if sorbet/config is present
2216
match worktree.read_text_file("sorbet/config") {
2317
Ok(_) => {
2418
// Config file exists, prefer custom arguments if available.
2519
binary_settings
2620
.and_then(|bs| bs.arguments)
27-
.unwrap_or(default_args)
21+
.unwrap_or_else(|| {
22+
vec![
23+
"tc".to_string(),
24+
"--lsp".to_string(),
25+
"--enable-experimental-lsp-document-highlight".to_string(),
26+
]
27+
})
2828
}
2929
Err(_) => {
3030
// gross, but avoid sorbet errors in a non-sorbet

src/ruby.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,21 @@ impl zed::Extension for RubyExtension {
122122
_: Option<String>,
123123
worktree: &Worktree,
124124
) -> Result<DebugAdapterBinary, String> {
125+
let shell_env = worktree.shell_env();
126+
let env_vars: Vec<(&str, &str)> = shell_env
127+
.iter()
128+
.map(|(key, value)| (key.as_str(), value.as_str()))
129+
.collect();
130+
125131
let mut rdbg_path = Path::new(&adapter_name)
126132
.join("rdbg")
127133
.to_string_lossy()
128134
.into_owned();
129135
let mut use_bundler = false;
130136

131137
if worktree.which(&rdbg_path).is_none() {
132-
let bundler = Bundler::new(
133-
worktree.root_path(),
134-
worktree.shell_env(),
135-
Box::new(RealCommandExecutor),
136-
);
137-
match bundler.installed_gem_version("debug") {
138+
let bundler = Bundler::new(worktree.root_path(), Box::new(RealCommandExecutor));
139+
match bundler.installed_gem_version("debug", &env_vars) {
138140
Ok(_version) => {
139141
rdbg_path = worktree
140142
.which("bundle")

0 commit comments

Comments
 (0)