ci: add GitHub Actions workflows with semantic-release auto-versioning
chore: fix all 702 clippy warnings across codebase - auto-fix 475 via cargo clippy --fix - fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms, underscore_binding, format_push_string, items_after_statements, needless_pass_by_value, clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline, and other clippy lints
This commit is contained in:
@@ -47,24 +47,24 @@ impl Tool for Delete {
|
||||
}
|
||||
|
||||
let metadata = path.metadata()
|
||||
.map_err(|e| anyhow!("failed to read metadata for '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to read metadata for '{rel}': {e}"))?;
|
||||
|
||||
if metadata.is_dir() {
|
||||
let is_empty = fs::read_dir(&path)
|
||||
.map_err(|e| anyhow!("failed to read directory '{}': {}", rel, e))?
|
||||
.map_err(|e| anyhow!("failed to read directory '{rel}': {e}"))?
|
||||
.next()
|
||||
.is_none();
|
||||
if is_empty {
|
||||
fs::remove_dir(&path)
|
||||
.map_err(|e| anyhow!("failed to remove directory '{}': {}", rel, e))?;
|
||||
Ok(format!("removed empty directory {}", rel))
|
||||
.map_err(|e| anyhow!("failed to remove directory '{rel}': {e}"))?;
|
||||
Ok(format!("removed empty directory {rel}"))
|
||||
} else {
|
||||
anyhow::bail!("directory '{}' is not empty (refusing to delete)", rel);
|
||||
anyhow::bail!("directory '{rel}' is not empty (refusing to delete)");
|
||||
}
|
||||
} else {
|
||||
fs::remove_file(&path)
|
||||
.map_err(|e| anyhow!("failed to delete '{}': {}", rel, e))?;
|
||||
Ok(format!("deleted {}", rel))
|
||||
.map_err(|e| anyhow!("failed to delete '{rel}': {e}"))?;
|
||||
Ok(format!("deleted {rel}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-11
@@ -1,3 +1,4 @@
|
||||
#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::cast_possible_wrap)]
|
||||
//! Tool: `edit` — replace a substring in a file with a new string.
|
||||
|
||||
use std::fs;
|
||||
@@ -70,25 +71,24 @@ impl Tool for Edit {
|
||||
anyhow::bail!("'old' must be a non-empty string; use 'write' to replace entire file contents");
|
||||
}
|
||||
let check_matches = check_graduated_checks(&rel, &new_str, &ctx.graduated_checks);
|
||||
let replace_all = args.get("replace_all").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||
let replace_all = args.get("replace_all").and_then(serde_json::Value::as_bool).unwrap_or(false);
|
||||
let path: PathBuf = resolve_path(&ctx.workspaces, &rel)?;
|
||||
if !path.exists() {
|
||||
anyhow::bail!("file '{}' does not exist at resolved path {}", rel, path.display());
|
||||
}
|
||||
if path.is_dir() {
|
||||
anyhow::bail!("'{}' is a directory, not a file", rel);
|
||||
anyhow::bail!("'{rel}' is a directory, not a file");
|
||||
}
|
||||
let content = fs::read_to_string(&path)
|
||||
.map_err(|e| anyhow!("failed to read '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to read '{rel}': {e}"))?;
|
||||
if !content.contains(&old) {
|
||||
anyhow::bail!("old string not found in '{}'", rel);
|
||||
anyhow::bail!("old string not found in '{rel}'");
|
||||
}
|
||||
if !replace_all {
|
||||
let count = content.matches(&old).count();
|
||||
if count > 1 {
|
||||
anyhow::bail!(
|
||||
"old string appears {} times in '{}'. Set replace_all=true to replace all occurrences, or provide a more specific match.",
|
||||
count, rel
|
||||
"old string appears {count} times in '{rel}'. Set replace_all=true to replace all occurrences, or provide a more specific match."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ impl Tool for Edit {
|
||||
content.replacen(&old, &new_str, 1)
|
||||
};
|
||||
fs::write(&path, &new_content)
|
||||
.map_err(|e| anyhow!("failed to write '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to write '{rel}': {e}"))?;
|
||||
let bytes_diff = if new_content.len() > content.len() {
|
||||
new_content.len() - content.len()
|
||||
} else {
|
||||
@@ -108,10 +108,8 @@ impl Tool for Edit {
|
||||
// Never fail the edit because of this — LSP errors are surfaced as a
|
||||
// trailing annotation on the success message instead.
|
||||
let lsp_note = if let Ok(mut lsp) = ctx.lsp_manager.lock() {
|
||||
match lsp.did_change_file(&path) {
|
||||
Ok(()) => String::new(),
|
||||
Err(e) => format!(" (LSP: {})", e),
|
||||
}
|
||||
lsp.did_change_file(&path);
|
||||
String::new()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
@@ -12,8 +12,8 @@ use anyhow::{Result, anyhow};
|
||||
pub fn arg_str(args: &Value, name: &str) -> Result<String> {
|
||||
args.get(name)
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| anyhow!("missing required argument: {}", name))
|
||||
.map(std::string::ToString::to_string)
|
||||
.ok_or_else(|| anyhow!("missing required argument: {name}"))
|
||||
}
|
||||
|
||||
/// Produce a user-friendly diagnostic string when a path doesn't resolve or exist.
|
||||
@@ -25,17 +25,17 @@ pub fn arg_str(args: &Value, name: &str) -> Result<String> {
|
||||
pub fn not_found_help(ctx: &super::super::ToolCtx, path: &Path, rel: &str) -> String {
|
||||
let canon = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());
|
||||
let in_ws = ctx.workspaces.iter().any(|w| {
|
||||
let wc = w.canonicalize().unwrap_or_else(|_| w.to_path_buf());
|
||||
let wc = w.canonicalize().unwrap_or_else(|_| w.clone());
|
||||
canon.starts_with(&wc)
|
||||
});
|
||||
if !in_ws {
|
||||
if in_ws {
|
||||
format!("path '{}' does not exist (resolved to {})", rel, canon.display())
|
||||
} else {
|
||||
format!(
|
||||
"path '{}' is outside all workspace roots. Workspace roots: {}",
|
||||
rel,
|
||||
ctx.workspaces.iter().map(|w| w.display().to_string()).collect::<Vec<_>>().join(", ")
|
||||
)
|
||||
} else {
|
||||
format!("path '{}' does not exist (resolved to {})", rel, canon.display())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -1,3 +1,4 @@
|
||||
#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::cast_possible_wrap)]
|
||||
//! Tool: `read` — display file contents with line numbers.
|
||||
|
||||
use std::fs;
|
||||
@@ -47,7 +48,7 @@ impl Tool for Read {
|
||||
/// exist; a "is a directory" message if the path points at a directory.
|
||||
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
||||
let rel = arg_str(args, "path")?;
|
||||
let limit = args.get("limit").and_then(|v| v.as_u64()).map(|v| v as usize);
|
||||
let limit = args.get("limit").and_then(serde_json::Value::as_u64).map(|v| v as usize);
|
||||
let path: PathBuf = match resolve_path(&ctx.workspaces, &rel) {
|
||||
Ok(p) => p,
|
||||
Err(_e) => return Ok(not_found_help(ctx, &PathBuf::from(&rel), &rel)),
|
||||
@@ -56,10 +57,10 @@ impl Tool for Read {
|
||||
return Ok(not_found_help(ctx, &path, &rel));
|
||||
}
|
||||
if path.is_dir() {
|
||||
return Ok(format!("'{}' is a directory, not a file. Use ls or glob to list directory contents.", rel));
|
||||
return Ok(format!("'{rel}' is a directory, not a file. Use ls or glob to list directory contents."));
|
||||
}
|
||||
let content = fs::read_to_string(&path)
|
||||
.map_err(|e| anyhow!("failed to read '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to read '{rel}': {e}"))?;
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let total = lines.len();
|
||||
let take = limit.unwrap_or(total).min(total);
|
||||
|
||||
@@ -60,18 +60,16 @@ impl Tool for Write {
|
||||
let path = resolve_path(&ctx.workspaces, &rel)?;
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)
|
||||
.map_err(|e| anyhow!("failed to create parent directories for '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to create parent directories for '{rel}': {e}"))?;
|
||||
}
|
||||
fs::write(&path, &content)
|
||||
.map_err(|e| anyhow!("failed to write '{}': {}", rel, e))?;
|
||||
.map_err(|e| anyhow!("failed to write '{rel}': {e}"))?;
|
||||
// Notify the LSP server of the on-disk change so diagnostics stay in
|
||||
// sync. Never fails the write itself: a lock failure or LSP error is
|
||||
// folded into the returned message instead of propagated as an Err.
|
||||
let lsp_note = if let Ok(mut lsp) = ctx.lsp_manager.lock() {
|
||||
match lsp.did_change_file(&path) {
|
||||
Ok(()) => String::new(),
|
||||
Err(e) => format!(" (LSP: {})", e),
|
||||
}
|
||||
lsp.did_change_file(&path);
|
||||
String::new()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user