Skip to content

Commit daaa4d7

Browse files
cli: Fix non-idiomatic Rust patterns across services
Replace `let _ = ` patterns that silently ignore Results with proper error handling using `if let Err(e)` and warning logs. Convert `.to_string()` calls on string literals to `String::from()` for clarity. Add explanatory comments for `.clone()` calls that are required by borrow checker constraints. Co-authored-by: SCE <sce@crocoder.dev>
1 parent 2c63aa5 commit daaa4d7

File tree

13 files changed

+83
-236
lines changed

13 files changed

+83
-236
lines changed

cli/build.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,23 @@ fn generate_embedded_asset_manifest() -> io::Result<()> {
105105
collect_files(&source_root, &source_root, &mut files)?;
106106
files.sort_unstable_by(|a, b| a.relative_path.cmp(&b.relative_path));
107107

108-
let _ = writeln!(
108+
writeln!(
109109
output,
110110
"pub static {}: &[EmbeddedAsset] = &[",
111111
target.const_name
112-
);
112+
)
113+
.expect("writing to String buffer should never fail");
113114

114115
for file in &files {
115116
println!("cargo:rerun-if-changed={}", file.absolute_path.display());
116-
let _ = writeln!(
117+
writeln!(
117118
output,
118119
" EmbeddedAsset {{ relative_path: \"{}\", bytes: include_bytes!(concat!(env!(\"CARGO_MANIFEST_DIR\"), \"{}{}\")) }},",
119120
escape_for_rust_string(&file.relative_path),
120121
target.include_prefix,
121122
escape_for_rust_string(&file.relative_path),
122-
);
123+
)
124+
.expect("writing to String buffer should never fail");
123125
}
124126

125127
output.push_str("];\n\n");

cli/src/app.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ where
122122
let styled_message =
123123
services::style::error_text(&services::security::redact_sensitive_text(&rendered));
124124

125-
let _ = writeln!(writer, "{styled_heading} [{styled_code}]: {styled_message}");
125+
writeln!(writer, "{styled_heading} [{styled_code}]: {styled_message}")
126+
.expect("writing error diagnostic to writer should not fail");
126127
}
127128

128129
#[allow(clippy::too_many_lines)]
@@ -345,7 +346,7 @@ fn classify_clap_error(error: &clap::Error) -> ClassifiedError {
345346
}
346347

347348
fn render_subcommand_help_from_args(args: &[String]) -> Option<(String, String)> {
348-
let command_name = args.get(1)?.clone();
349+
let command_name = args.get(1)?.to_owned();
349350
let command_path = args[1..]
350351
.iter()
351352
.take_while(|arg| !arg.starts_with('-'))
@@ -651,10 +652,11 @@ fn dispatch(
651652
) -> Result<String, ClassifiedError> {
652653
match command {
653654
Command::Help => Ok(command_surface::help_text()),
654-
Command::HelpText { text, .. } => Ok(text.clone()),
655+
Command::HelpText { text, .. } => Ok(text.to_owned()),
655656
Command::Auth(request) => services::auth_command::run_auth_subcommand(*request)
656657
.map_err(|error| ClassifiedError::runtime(error.to_string())),
657658
Command::Completion(request) => Ok(services::completion::render_completion(*request)),
659+
// Clone required: run_config_subcommand takes ownership of ConfigSubcommand
658660
Command::Config(subcommand) => services::config::run_config_subcommand(subcommand.clone())
659661
.map_err(|error| ClassifiedError::runtime(error.to_string())),
660662
Command::Setup(request) => {
@@ -710,8 +712,10 @@ fn dispatch(
710712
}
711713
Command::Doctor(request) => services::doctor::run_doctor(*request)
712714
.map_err(|error| ClassifiedError::runtime(error.to_string())),
715+
// Clone required: run_hooks_subcommand takes ownership of HookSubcommand
713716
Command::Hooks(subcommand) => services::hooks::run_hooks_subcommand(subcommand.clone())
714717
.map_err(|error| ClassifiedError::runtime(error.to_string())),
718+
// Clone required: run_trace_subcommand takes ownership of TraceRequest
715719
Command::Trace(request) => services::trace::run_trace_subcommand(request.clone())
716720
.map_err(|error| ClassifiedError::runtime(error.to_string())),
717721
Command::Sync(request) => services::sync::run_placeholder_sync(*request)

cli/src/cli_schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub fn render_help_for_path(path: &[&str]) -> Option<String> {
2424
let mut command = Cli::command();
2525

2626
for segment in path {
27+
// Clone required: find_subcommand_mut returns a mutable reference that cannot
28+
// be kept alive across loop iterations, so we must clone to get an owned value
2729
command = command.find_subcommand_mut(segment)?.clone();
2830
}
2931

cli/src/command_surface.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub fn help_text() -> String {
9797
command_name(command.name),
9898
command.purpose
9999
)
100-
.unwrap();
100+
.expect("writing to String should never fail");
101101
}
102102

103103
format!(

cli/src/services/auth.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub struct TokenResponse {
6666
}
6767

6868
fn default_token_type() -> String {
69-
"Bearer".to_string()
69+
String::from("Bearer")
7070
}
7171

7272
fn default_access_token_expires_in() -> u64 {
@@ -193,7 +193,7 @@ pub async fn ensure_valid_token(
193193

194194
let Some(stored) = load_tokens()? else {
195195
return Err(AuthError::Unauthorized(
196-
"No stored WorkOS credentials were found. Try: run 'sce login' before running authenticated commands.".to_string(),
196+
String::from("No stored WorkOS credentials were found. Try: run 'sce login' before running authenticated commands."),
197197
));
198198
};
199199

@@ -220,7 +220,7 @@ pub async fn renew_stored_token(
220220

221221
let Some(stored) = load_tokens()? else {
222222
return Err(AuthError::Unauthorized(
223-
"No stored WorkOS credentials were found. Try: run 'sce auth login' before running authenticated commands.".to_string(),
223+
String::from("No stored WorkOS credentials were found. Try: run 'sce auth login' before running authenticated commands."),
224224
));
225225
};
226226

@@ -257,9 +257,9 @@ pub async fn request_device_authorization(
257257
|| parsed.user_code.trim().is_empty()
258258
|| parsed.verification_uri.trim().is_empty()
259259
{
260-
return Err(AuthError::InvalidResponse(
261-
"device authorization response is missing required fields".to_string(),
262-
));
260+
return Err(AuthError::InvalidResponse(String::from(
261+
"device authorization response is missing required fields",
262+
)));
263263
}
264264
return Ok(parsed);
265265
}
@@ -299,7 +299,7 @@ async fn poll_for_device_token(
299299
attempts = attempts.saturating_add(1);
300300
if attempts > max_polls {
301301
return Err(AuthError::Unauthorized(
302-
"WorkOS device authorization expired before approval completed. Try: run 'sce login' again and complete verification before the code expires.".to_string(),
302+
String::from("WorkOS device authorization expired before approval completed. Try: run 'sce login' again and complete verification before the code expires."),
303303
));
304304
}
305305

cli/src/services/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,12 +1677,12 @@ fn format_bash_policies_text(value: &ResolvedOptionalValue<BashPolicyConfig>) ->
16771677
match (value.value.as_ref(), value.source) {
16781678
(Some(config), Some(source)) => {
16791679
let presets = if config.presets.is_empty() {
1680-
"(none)".to_string()
1680+
String::from("(none)")
16811681
} else {
16821682
config.presets.join(", ")
16831683
};
16841684
let custom = if config.custom.is_empty() {
1685-
"(none)".to_string()
1685+
String::from("(none)")
16861686
} else {
16871687
config
16881688
.custom
@@ -1918,7 +1918,7 @@ fn format_optional_auth_resolved_value_json(
19181918

19191919
fn format_text_display_value(key: &str, value: &str) -> String {
19201920
if should_fully_redact_text_value(key) {
1921-
return "[REDACTED]".to_string();
1921+
return String::from("[REDACTED]");
19221922
}
19231923

19241924
if looks_credential_like(value) {

cli/src/services/doctor.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,8 @@ fn build_report_with_dependencies(
289289
category: ProblemCategory::RepositoryTargeting,
290290
severity: ProblemSeverity::Error,
291291
fixability: ProblemFixability::ManualOnly,
292-
summary: "Git is not available on this machine.".to_string(),
293-
remediation: "Install an accessible 'git' binary and ensure it is on PATH before rerunning 'sce doctor'.".to_string(),
292+
summary: String::from("Git is not available on this machine."),
293+
remediation: String::from("Install an accessible 'git' binary and ensure it is on PATH before rerunning 'sce doctor'."),
294294
next_action: "manual_steps",
295295
});
296296
Vec::new()
@@ -299,8 +299,8 @@ fn build_report_with_dependencies(
299299
category: ProblemCategory::RepositoryTargeting,
300300
severity: ProblemSeverity::Error,
301301
fixability: ProblemFixability::ManualOnly,
302-
summary: "The current repository is bare and does not support local SCE hook rollout.".to_string(),
303-
remediation: "Run 'sce doctor' from a non-bare working tree clone to inspect repo-scoped SCE hook health.".to_string(),
302+
summary: String::from("The current repository is bare and does not support local SCE hook rollout."),
303+
remediation: String::from("Run 'sce doctor' from a non-bare working tree clone to inspect repo-scoped SCE hook health."),
304304
next_action: "manual_steps",
305305
});
306306
Vec::new()
@@ -309,8 +309,8 @@ fn build_report_with_dependencies(
309309
category: ProblemCategory::RepositoryTargeting,
310310
severity: ProblemSeverity::Error,
311311
fixability: ProblemFixability::ManualOnly,
312-
summary: "The current directory is not inside a git repository.".to_string(),
313-
remediation: "Run 'sce doctor' from inside the target repository working tree to inspect repo-scoped SCE hook health.".to_string(),
312+
summary: String::from("The current directory is not inside a git repository."),
313+
remediation: String::from("Run 'sce doctor' from inside the target repository working tree to inspect repo-scoped SCE hook health."),
314314
next_action: "manual_steps",
315315
});
316316
Vec::new()
@@ -321,8 +321,8 @@ fn build_report_with_dependencies(
321321
category: ProblemCategory::RepositoryTargeting,
322322
severity: ProblemSeverity::Error,
323323
fixability: ProblemFixability::ManualOnly,
324-
summary: "Unable to resolve git hooks directory.".to_string(),
325-
remediation: "Verify that git repository inspection succeeds and rerun 'sce doctor' inside a non-bare git repository.".to_string(),
324+
summary: String::from("Unable to resolve git hooks directory."),
325+
remediation: String::from("Verify that git repository inspection succeeds and rerun 'sce doctor' inside a non-bare git repository."),
326326
next_action: "manual_steps",
327327
});
328328
Vec::new()
@@ -379,7 +379,7 @@ fn collect_global_state_health(
379379
severity: ProblemSeverity::Error,
380380
fixability: ProblemFixability::ManualOnly,
381381
summary: format!("Unable to resolve expected state root: {error}"),
382-
remediation: "Verify that the current platform exposes a writable SCE state directory before rerunning 'sce doctor'.".to_string(),
382+
remediation: String::from("Verify that the current platform exposes a writable SCE state directory before rerunning 'sce doctor'."),
383383
next_action: "manual_steps",
384384
}),
385385
}
@@ -415,7 +415,7 @@ fn collect_global_state_health(
415415
severity: ProblemSeverity::Error,
416416
fixability: ProblemFixability::ManualOnly,
417417
summary: format!("Unable to resolve expected global config path: {error}"),
418-
remediation: "Verify that the current platform exposes a writable SCE config directory before rerunning 'sce doctor'.".to_string(),
418+
remediation: String::from("Verify that the current platform exposes a writable SCE config directory before rerunning 'sce doctor'."),
419419
next_action: "manual_steps",
420420
}),
421421
}
@@ -465,7 +465,7 @@ fn collect_global_state_health(
465465
severity: ProblemSeverity::Error,
466466
fixability: ProblemFixability::ManualOnly,
467467
summary: format!("Unable to resolve expected Agent Trace local DB path: {error}"),
468-
remediation: "Verify that the SCE state root can be resolved on this machine before rerunning 'sce doctor'.".to_string(),
468+
remediation: String::from("Verify that the SCE state root can be resolved on this machine before rerunning 'sce doctor'."),
469469
next_action: "manual_steps",
470470
});
471471
None
@@ -493,7 +493,7 @@ fn inspect_agent_trace_db_health(
493493
"Agent Trace local DB path '{}' has no parent directory.",
494494
db_health.path.display()
495495
),
496-
remediation: "Verify that the SCE state root resolves to a normal filesystem path before rerunning 'sce doctor'.".to_string(),
496+
remediation: String::from("Verify that the SCE state root resolves to a normal filesystem path before rerunning 'sce doctor'."),
497497
next_action: "manual_steps",
498498
});
499499
return;
@@ -1241,7 +1241,7 @@ fn run_auto_fixes(
12411241
fix_results.push(DoctorFixResultRecord {
12421242
category: ProblemCategory::HookRollout,
12431243
outcome: FixResult::Failed,
1244-
detail: "Automatic hook repair could not start because the repository root was not resolved during diagnosis.".to_string(),
1244+
detail: String::from("Automatic hook repair could not start because the repository root was not resolved during diagnosis."),
12451245
});
12461246
return fix_results;
12471247
};
@@ -1273,7 +1273,7 @@ fn run_filesystem_auto_fixes(
12731273
return vec![DoctorFixResultRecord {
12741274
category: ProblemCategory::FilesystemPermissions,
12751275
outcome: FixResult::Failed,
1276-
detail: "Automatic Agent Trace directory repair could not start because the expected local DB path was not resolved during diagnosis.".to_string(),
1276+
detail: String::from("Automatic Agent Trace directory repair could not start because the expected local DB path was not resolved during diagnosis."),
12771277
}];
12781278
};
12791279

@@ -1464,7 +1464,10 @@ mod tests {
14641464

14651465
impl Drop for TestDir {
14661466
fn drop(&mut self) {
1467-
let _ = fs::remove_dir_all(&self.path);
1467+
// Best-effort cleanup in test harness; ignore errors since we can't propagate them
1468+
if let Err(e) = fs::remove_dir_all(&self.path) {
1469+
eprintln!("Warning: Failed to clean up test directory: {e}");
1470+
}
14681471
}
14691472
}
14701473

@@ -1489,7 +1492,7 @@ mod tests {
14891492
severity: ProblemSeverity::Error,
14901493
fixability: ProblemFixability::AutoFixable,
14911494
summary: summary.to_string(),
1492-
remediation: "Run 'sce doctor --fix'.".to_string(),
1495+
remediation: String::from("Run 'sce doctor --fix'."),
14931496
next_action: "doctor_fix",
14941497
}
14951498
}

cli/src/services/hooks.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ fn run_git_command(repository_root: &Path, args: &[&str], context_message: &str)
183183
if !output.status.success() {
184184
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
185185
let diagnostic = if stderr.is_empty() {
186-
"git command exited with a non-zero status".to_string()
186+
String::from("git command exited with a non-zero status")
187187
} else {
188188
stderr
189189
};
@@ -215,7 +215,7 @@ fn run_git_command_allow_empty(
215215
if !output.status.success() {
216216
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
217217
let diagnostic = if stderr.is_empty() {
218-
"git command exited with a non-zero status".to_string()
218+
String::from("git command exited with a non-zero status")
219219
} else {
220220
stderr
221221
};
@@ -262,9 +262,11 @@ fn collect_pending_checkpoint(repository_root: &Path) -> Result<PendingCheckpoin
262262

263263
let mut all_paths = HashSet::new();
264264
for path in staged_ranges.keys() {
265+
// Clone required: HashSet::insert requires ownership of the key
265266
all_paths.insert(path.clone());
266267
}
267268
for path in unstaged_ranges.keys() {
269+
// Clone required: HashSet::insert requires ownership of the key
268270
all_paths.insert(path.clone());
269271
}
270272

@@ -959,7 +961,7 @@ fn load_post_commit_checkpoint_files(repository_root: &Path) -> Result<Vec<FileA
959961
files.push(FileAttributionInput {
960962
path: path.to_string(),
961963
conversations: vec![ConversationInput {
962-
url: "https://crocoder.dev/sce/local-hooks/pre-commit-checkpoint".to_string(),
964+
url: String::from("https://crocoder.dev/sce/local-hooks/pre-commit-checkpoint"),
963965
related: Vec::new(),
964966
ranges,
965967
}],
@@ -2104,7 +2106,7 @@ async fn write_trace_record_to_local_db(
21042106
.metadata
21052107
.get(METADATA_QUALITY_STATUS)
21062108
.cloned()
2107-
.unwrap_or_else(|| "final".to_string());
2109+
.unwrap_or_else(|| String::from("final"));
21082110

21092111
conn.execute(
21102112
"INSERT INTO trace_records (repository_id, commit_id, trace_id, version, content_type, notes_ref, payload_json, quality_status, idempotency_key, recorded_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
@@ -2326,7 +2328,10 @@ impl TraceEmissionLedger for FileTraceEmissionLedger {
23262328
.append(true)
23272329
.open(&self.path)
23282330
{
2329-
let _ = writeln!(file, "{commit_sha}");
2331+
// Best-effort append to notes; errors are logged but don't fail the commit
2332+
if let Err(e) = writeln!(file, "{commit_sha}") {
2333+
eprintln!("Warning: Failed to append commit SHA to notes: {e}");
2334+
}
23302335
}
23312336
}
23322337
}

cli/src/services/observability.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ impl TelemetryRuntime {
107107
) -> Result<Self> {
108108
Self::from_config(&TelemetryConfig {
109109
enabled: config.otel_enabled,
110+
// Clone required: TelemetryConfig owns the endpoint String
110111
endpoint: config.otel_endpoint.clone(),
111112
protocol: match config.otel_protocol {
112113
config::OtlpProtocol::Grpc => OtlpProtocol::Grpc,
@@ -132,11 +133,13 @@ impl TelemetryRuntime {
132133
let exporter = match config.protocol {
133134
OtlpProtocol::Grpc => opentelemetry_otlp::SpanExporter::builder()
134135
.with_tonic()
136+
// Clone required: with_endpoint takes ownership of the endpoint String
135137
.with_endpoint(config.endpoint.clone())
136138
.build()
137139
.map_err(|error| anyhow!("Failed to initialize OTLP gRPC exporter: {error}"))?,
138140
OtlpProtocol::HttpProtobuf => opentelemetry_otlp::SpanExporter::builder()
139141
.with_http()
142+
// Clone required: with_endpoint takes ownership of the endpoint String
140143
.with_endpoint(config.endpoint.clone())
141144
.build()
142145
.map_err(|error| anyhow!("Failed to initialize OTLP HTTP exporter: {error}"))?,
@@ -169,7 +172,10 @@ impl TelemetryRuntime {
169172
impl Drop for TelemetryRuntime {
170173
fn drop(&mut self) {
171174
if let Some(provider) = self.provider.take() {
172-
let _ = provider.shutdown();
175+
// Best-effort shutdown during drop; errors are logged but not propagated
176+
if let Err(e) = provider.shutdown() {
177+
eprintln!("Warning: Failed to shutdown telemetry provider: {e:?}");
178+
}
173179
}
174180
}
175181
}

cli/src/services/security.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@ pub fn ensure_directory_is_writable(path: &std::path::Path, context: &str) -> Re
171171
)
172172
})?;
173173

174-
let _ = std::fs::remove_file(&probe_path);
174+
// Clean up probe file; best-effort with warning on failure
175+
if let Err(e) = std::fs::remove_file(&probe_path) {
176+
eprintln!(
177+
"Warning: Failed to clean up write-probe file '{}': {}",
178+
probe_path.display(),
179+
e
180+
);
181+
}
175182
Ok(())
176183
}
177184

0 commit comments

Comments
 (0)