Skip to content

Commit d66dd2f

Browse files
committed
♻️ Apply clippy suggestions for idiomatic Rust patterns
Refactor codebase to use more idiomatic Rust patterns based on clippy recommendations across 15 files: - Replace manual sorting with sort_by_key in session.rs for clearer intent - Replace map().unwrap_or() chains with is_ok_and() for cleaner predicate checks in watcher.rs - Use checked_div() with unwrap_or() fallback for scroll percentage calculations in changelog, pr, release_notes, and review render modules - Reformat long function signatures and error map closures for better readability in provider.rs and parallel_analyze.rs - Split complex Style::default() chains across multiple lines in theme selector, settings modal, and ui.rs for improved maintainability - Use std::time::Duration::from_secs() instead of from_millis(2000) These changes improve code clarity and leverage Rust's standard patterns without altering functionality. All formatting follows rustfmt conventions.
1 parent 3531fb3 commit d66dd2f

File tree

15 files changed

+150
-78
lines changed

15 files changed

+150
-78
lines changed

src/agents/provider.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ fn validate_and_warn(key: &str, provider: Provider, source: &str) {
9393
/// Note: An empty string in config is treated as "not configured" and falls
9494
/// back to the environment variable. This allows users to override env vars
9595
/// in config while still supporting env-only setups.
96-
pub fn resolve_api_key(api_key: Option<&str>, provider: Provider) -> (Option<String>, ApiKeySource) {
96+
pub fn resolve_api_key(
97+
api_key: Option<&str>,
98+
provider: Provider,
99+
) -> (Option<String>, ApiKeySource) {
97100
// If explicit key provided and non-empty, use it
98101
if let Some(key) = api_key
99102
&& !key.is_empty()
@@ -146,9 +149,11 @@ pub fn openai_builder(model: &str, api_key: Option<&str>) -> Result<OpenAIBuilde
146149
let client = match resolved_key {
147150
Some(key) => openai::Client::new(&key)
148151
// Sanitize error to prevent potential key exposure in error messages
149-
.map_err(|_| anyhow::anyhow!(
150-
"Failed to create OpenAI client: authentication or configuration error"
151-
))?,
152+
.map_err(|_| {
153+
anyhow::anyhow!(
154+
"Failed to create OpenAI client: authentication or configuration error"
155+
)
156+
})?,
152157
None => openai::Client::from_env(),
153158
};
154159
Ok(client.completions_api().agent(model))
@@ -173,9 +178,11 @@ pub fn anthropic_builder(model: &str, api_key: Option<&str>) -> Result<Anthropic
173178
let client = match resolved_key {
174179
Some(key) => anthropic::Client::new(&key)
175180
// Sanitize error to prevent potential key exposure in error messages
176-
.map_err(|_| anyhow::anyhow!(
177-
"Failed to create Anthropic client: authentication or configuration error"
178-
))?,
181+
.map_err(|_| {
182+
anyhow::anyhow!(
183+
"Failed to create Anthropic client: authentication or configuration error"
184+
)
185+
})?,
179186
None => anthropic::Client::from_env(),
180187
};
181188
Ok(client.agent(model))
@@ -200,9 +207,11 @@ pub fn gemini_builder(model: &str, api_key: Option<&str>) -> Result<GeminiBuilde
200207
let client = match resolved_key {
201208
Some(key) => gemini::Client::new(&key)
202209
// Sanitize error to prevent potential key exposure in error messages
203-
.map_err(|_| anyhow::anyhow!(
204-
"Failed to create Gemini client: authentication or configuration error"
205-
))?,
210+
.map_err(|_| {
211+
anyhow::anyhow!(
212+
"Failed to create Gemini client: authentication or configuration error"
213+
)
214+
})?,
206215
None => gemini::Client::from_env(),
207216
};
208217
Ok(client.agent(model))
@@ -215,8 +224,7 @@ mod tests {
215224
#[test]
216225
fn test_resolve_api_key_uses_config_when_provided() {
217226
// Config key takes precedence
218-
let (key, source) =
219-
resolve_api_key(Some("sk-config-key-1234567890"), Provider::OpenAI);
227+
let (key, source) = resolve_api_key(Some("sk-config-key-1234567890"), Provider::OpenAI);
220228
assert_eq!(key, Some("sk-config-key-1234567890".to_string()));
221229
assert_eq!(source, ApiKeySource::Config);
222230
}
@@ -265,8 +273,7 @@ mod tests {
265273
fn test_resolve_api_key_all_providers() {
266274
// Test that resolve_api_key works for all supported providers
267275
for provider in Provider::ALL {
268-
let (key, source) =
269-
resolve_api_key(Some("test-key-123456789012345"), *provider);
276+
let (key, source) = resolve_api_key(Some("test-key-123456789012345"), *provider);
270277
assert_eq!(key, Some("test-key-123456789012345".to_string()));
271278
assert_eq!(source, ApiKeySource::Config);
272279
}

src/agents/status_messages.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -348,11 +348,8 @@ impl StatusMessageGenerator {
348348
async fn generate_completion_internal(&self, context: &StatusContext) -> Result<StatusMessage> {
349349
let prompt = Self::build_completion_prompt(context);
350350

351-
let agent = Self::build_status_agent(
352-
&self.provider,
353-
&self.fast_model,
354-
self.api_key.as_deref(),
355-
)?;
351+
let agent =
352+
Self::build_status_agent(&self.provider, &self.fast_model, self.api_key.as_deref())?;
356353
let response = agent.prompt(&prompt).await?;
357354
let message = capitalize_first(response.trim());
358355

src/agents/tools/parallel_analyze.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,11 @@ impl SubagentRunner {
117117
match resolved_key {
118118
Some(key) => openai::Client::new(&key)
119119
// Sanitize error to avoid exposing key material
120-
.map_err(|_| anyhow::anyhow!("Failed to create OpenAI client: authentication or configuration error")),
120+
.map_err(|_| {
121+
anyhow::anyhow!(
122+
"Failed to create OpenAI client: authentication or configuration error"
123+
)
124+
}),
121125
None => Ok(openai::Client::from_env()),
122126
}
123127
}
@@ -131,7 +135,11 @@ impl SubagentRunner {
131135
match resolved_key {
132136
Some(key) => anthropic::Client::new(&key)
133137
// Sanitize error to avoid exposing key material
134-
.map_err(|_| anyhow::anyhow!("Failed to create Anthropic client: authentication or configuration error")),
138+
.map_err(|_| {
139+
anyhow::anyhow!(
140+
"Failed to create Anthropic client: authentication or configuration error"
141+
)
142+
}),
135143
None => Ok(anthropic::Client::from_env()),
136144
}
137145
}
@@ -145,7 +153,11 @@ impl SubagentRunner {
145153
match resolved_key {
146154
Some(key) => gemini::Client::new(&key)
147155
// Sanitize error to avoid exposing key material
148-
.map_err(|_| anyhow::anyhow!("Failed to create Gemini client: authentication or configuration error")),
156+
.map_err(|_| {
157+
anyhow::anyhow!(
158+
"Failed to create Gemini client: authentication or configuration error"
159+
)
160+
}),
149161
None => Ok(gemini::Client::from_env()),
150162
}
151163
}

src/companion/session.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl SessionState {
102102
/// Get files ordered by most recently touched
103103
pub fn recent_files(&self) -> Vec<&FileActivity> {
104104
let mut files: Vec<_> = self.files_touched.values().collect();
105-
files.sort_by(|a, b| b.last_touched.cmp(&a.last_touched));
105+
files.sort_by_key(|f| std::cmp::Reverse(f.last_touched));
106106
files
107107
}
108108

src/companion/watcher.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,12 @@ impl FileWatcherService {
114114
for event in events {
115115
// Check for git ref changes (HEAD, refs, index)
116116
let is_git_ref_change = event.paths.iter().any(|p| {
117-
p.strip_prefix(repo_path)
118-
.map(|rel| {
119-
let rel_str = rel.to_string_lossy();
120-
rel_str == ".git/HEAD"
121-
|| rel_str.starts_with(".git/refs/")
122-
|| rel_str == ".git/index"
123-
})
124-
.unwrap_or(false)
117+
p.strip_prefix(repo_path).is_ok_and(|rel| {
118+
let rel_str = rel.to_string_lossy();
119+
rel_str == ".git/HEAD"
120+
|| rel_str.starts_with(".git/refs/")
121+
|| rel_str == ".git/index"
122+
})
125123
});
126124

127125
if is_git_ref_change {

src/providers.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,7 @@ mod tests {
316316
#[test]
317317
fn test_api_key_validation_valid_openai_project_key() {
318318
// Valid OpenAI project key format (starts with sk-proj-, long enough)
319-
let result =
320-
Provider::OpenAI.validate_api_key_format("sk-proj-1234567890abcdefghijklmnop");
319+
let result = Provider::OpenAI.validate_api_key_format("sk-proj-1234567890abcdefghijklmnop");
321320
assert!(result.is_ok());
322321
}
323322

src/studio/app/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,7 @@ impl StudioApp {
14851485

14861486
tokio::spawn(async move {
14871487
match tokio::time::timeout(
1488-
std::time::Duration::from_millis(2000),
1488+
std::time::Duration::from_secs(2),
14891489
status_gen.generate_completion(&context),
14901490
)
14911491
.await

src/studio/render/changelog.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ fn scrollable_title(base_title: &str, scroll: usize, total_lines: usize, visible
1616
format!(" {} ", base_title)
1717
} else {
1818
let max_scroll = total_lines.saturating_sub(visible);
19-
let percent = if max_scroll == 0 {
20-
100
21-
} else {
22-
((scroll.min(max_scroll)) * 100) / max_scroll
23-
};
19+
let percent = ((scroll.min(max_scroll)) * 100)
20+
.checked_div(max_scroll)
21+
.unwrap_or(100);
2422
format!(
2523
" {} ({}/{}) {}% ",
2624
base_title,

src/studio/render/modals/settings.rs

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,10 @@ fn render_theme_strip(frame: &mut Frame, area: Rect) {
161161
// Compact preview: palette swatches + gradient on one line
162162
let mut spans = vec![
163163
Span::styled(" ", Style::default()),
164-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY)))),
164+
Span::styled(
165+
"██",
166+
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
167+
),
165168
Span::styled(" ", Style::default()),
166169
Span::styled(
167170
"██",
@@ -173,12 +176,24 @@ fn render_theme_strip(frame: &mut Frame, area: Rect) {
173176
Style::default().fg(Color::from(t.color(tokens::ACCENT_TERTIARY))),
174177
),
175178
Span::styled(" ", Style::default()),
176-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::SUCCESS)))),
179+
Span::styled(
180+
"██",
181+
Style::default().fg(Color::from(t.color(tokens::SUCCESS))),
182+
),
177183
Span::styled(" ", Style::default()),
178-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::WARNING)))),
184+
Span::styled(
185+
"██",
186+
Style::default().fg(Color::from(t.color(tokens::WARNING))),
187+
),
179188
Span::styled(" ", Style::default()),
180-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::ERROR)))),
181-
Span::styled(" │ ", Style::default().fg(Color::from(t.color(tokens::TEXT_DIM)))),
189+
Span::styled(
190+
"██",
191+
Style::default().fg(Color::from(t.color(tokens::ERROR))),
192+
),
193+
Span::styled(
194+
" │ ",
195+
Style::default().fg(Color::from(t.color(tokens::TEXT_DIM))),
196+
),
182197
];
183198

184199
// Add gradient
@@ -210,12 +225,18 @@ fn render_footer(frame: &mut Frame, area: Rect, state: &SettingsState) {
210225

211226
let hints = if state.editing {
212227
Line::from(vec![
213-
Span::styled(" Enter", Style::default().fg(Color::from(t.color(tokens::SUCCESS)))),
228+
Span::styled(
229+
" Enter",
230+
Style::default().fg(Color::from(t.color(tokens::SUCCESS))),
231+
),
214232
Span::styled(
215233
" confirm ",
216234
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
217235
),
218-
Span::styled("Esc", Style::default().fg(Color::from(t.color(tokens::WARNING)))),
236+
Span::styled(
237+
"Esc",
238+
Style::default().fg(Color::from(t.color(tokens::WARNING))),
239+
),
219240
Span::styled(
220241
" cancel",
221242
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
@@ -227,8 +248,14 @@ fn render_footer(frame: &mut Frame, area: Rect, state: &SettingsState) {
227248
" ↑↓",
228249
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
229250
),
230-
Span::styled(" nav ", Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED)))),
231-
Span::styled("←→", Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY)))),
251+
Span::styled(
252+
" nav ",
253+
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
254+
),
255+
Span::styled(
256+
"←→",
257+
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
258+
),
232259
Span::styled(
233260
" cycle ",
234261
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
@@ -251,8 +278,14 @@ fn render_footer(frame: &mut Frame, area: Rect, state: &SettingsState) {
251278
" save ",
252279
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
253280
),
254-
Span::styled("Esc", Style::default().fg(Color::from(t.color(tokens::WARNING)))),
255-
Span::styled(" close", Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED)))),
281+
Span::styled(
282+
"Esc",
283+
Style::default().fg(Color::from(t.color(tokens::WARNING))),
284+
),
285+
Span::styled(
286+
" close",
287+
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
288+
),
256289
])
257290
};
258291

src/studio/render/modals/theme_selector.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ fn render_theme_list(
7676
" Filter: ",
7777
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
7878
),
79-
Span::styled(input, Style::default().fg(Color::from(t.color(tokens::TEXT_PRIMARY)))),
79+
Span::styled(
80+
input,
81+
Style::default().fg(Color::from(t.color(tokens::TEXT_PRIMARY))),
82+
),
8083
Span::styled(
8184
if input.is_empty() { "│" } else { "█" },
8285
Style::default().fg(Color::from(t.color(tokens::ACCENT_SECONDARY))),
@@ -154,7 +157,10 @@ fn render_theme_list(
154157
" ↑↓",
155158
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
156159
),
157-
Span::styled(" nav ", Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED)))),
160+
Span::styled(
161+
" nav ",
162+
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
163+
),
158164
Span::styled(
159165
"Enter",
160166
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
@@ -163,7 +169,10 @@ fn render_theme_list(
163169
" select ",
164170
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
165171
),
166-
Span::styled("Esc", Style::default().fg(Color::from(t.color(tokens::WARNING)))),
172+
Span::styled(
173+
"Esc",
174+
Style::default().fg(Color::from(t.color(tokens::WARNING))),
175+
),
167176
Span::styled(
168177
" cancel",
169178
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
@@ -207,7 +216,10 @@ fn render_theme_preview(
207216
)),
208217
// Author
209218
Line::from(vec![
210-
Span::styled(" by ", Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED)))),
219+
Span::styled(
220+
" by ",
221+
Style::default().fg(Color::from(t.color(tokens::TEXT_MUTED))),
222+
),
211223
Span::styled(
212224
&theme_info.author,
213225
Style::default().fg(Color::from(t.color(tokens::TEXT_SECONDARY))),
@@ -248,7 +260,10 @@ fn render_theme_preview(
248260
// Color swatches (using current theme since we apply live preview)
249261
lines.push(Line::from(vec![
250262
Span::styled(" ", Style::default()),
251-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY)))),
263+
Span::styled(
264+
"██",
265+
Style::default().fg(Color::from(t.color(tokens::ACCENT_PRIMARY))),
266+
),
252267
Span::raw(" "),
253268
Span::styled(
254269
"██",
@@ -260,11 +275,20 @@ fn render_theme_preview(
260275
Style::default().fg(Color::from(t.color(tokens::ACCENT_TERTIARY))),
261276
),
262277
Span::raw(" "),
263-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::SUCCESS)))),
278+
Span::styled(
279+
"██",
280+
Style::default().fg(Color::from(t.color(tokens::SUCCESS))),
281+
),
264282
Span::raw(" "),
265-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::WARNING)))),
283+
Span::styled(
284+
"██",
285+
Style::default().fg(Color::from(t.color(tokens::WARNING))),
286+
),
266287
Span::raw(" "),
267-
Span::styled("██", Style::default().fg(Color::from(t.color(tokens::ERROR)))),
288+
Span::styled(
289+
"██",
290+
Style::default().fg(Color::from(t.color(tokens::ERROR))),
291+
),
268292
]));
269293

270294
lines.push(Line::from(""));

0 commit comments

Comments
 (0)