fn similar_paths_for(err: &AtomwriteError, ctx: &ErrorContext) -> Option<Vec<String>> {
let path = match err {
AtomwriteError::NotFound { path } => path,
_ => return None,
};
let wanted = path.file_name()?.to_string_lossy().to_string();
if wanted.is_empty() {
return None;
}
let root = ctx.workspace.as_ref()?;
let mut builder = ignore::WalkBuilder::new(root);
builder.max_depth(Some(6));
crate::concurrency::apply_walk_threads(&mut builder, None);
let wanted_c = wanted;
let mut scored: Vec<(f64, String)> =
crate::concurrency::collect_mapped_parallel(&builder, move |entry| {
if !entry.file_type().is_some_and(|ft| ft.is_file()) {
return None;
}
let name = entry.file_name().to_string_lossy();
let score = strsim::jaro_winkler(&wanted_c, &name);
if score >= 0.75 {
Some((score, entry.path().display().to_string()))
} else {
None
}
});
scored.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
scored.truncate(5);
if scored.is_empty() {
None
} else {
Some(scored.into_iter().map(|(_, p)| p).collect())
}
}
#[derive(Debug, Default, Clone)]
pub struct ErrorContext {
pub workspace_provided: bool,
pub workspace: Option<PathBuf>,
}
#[cold]
fn suggestion_for(err: &AtomwriteError, ctx: &ErrorContext) -> Option<String> {
match err {
AtomwriteError::NotFound { .. } => Some(t!("suggestion.not-found").to_string()),
AtomwriteError::EditPairFailed { index, .. } => Some(
t!("suggestion.edit-pair-failed", index = index).to_string(),
),
AtomwriteError::InvalidInput { reason } => Some(
t!("suggestion.invalid-input", reason = reason.as_str()).to_string(),
),
AtomwriteError::PermissionDenied { .. } => {
Some(t!("suggestion.permission-denied").to_string())
}
AtomwriteError::DiskFull { .. } => Some(t!("suggestion.disk-full").to_string()),
AtomwriteError::QuotaExceeded { .. } => {
Some(t!("suggestion.quota-exceeded").to_string())
}
AtomwriteError::CrossDevice { .. } => Some(t!("suggestion.cross-device").to_string()),
AtomwriteError::Io { source } => {
Some(t!("suggestion.io", source = source.to_string()).to_string())
}
AtomwriteError::ConfigInvalid { reason } => Some(
t!("suggestion.config-invalid", reason = reason.as_str()).to_string(),
),
AtomwriteError::StateDrift { .. } => Some(t!("suggestion.state-drift").to_string()),
AtomwriteError::ChecksumVerifyFailed { .. } => {
Some(t!("suggestion.checksum-verify").to_string())
}
AtomwriteError::FileTooLarge { .. } => Some(t!("suggestion.file-too-large").to_string()),
AtomwriteError::WorkspaceJail { workspace, .. } => {
if ctx.workspace_provided {
Some(
t!(
"suggestion.workspace-jail-inside",
workspace = workspace.display().to_string()
)
.to_string(),
)
} else {
Some(t!("suggestion.workspace-jail-set").to_string())
}
}
AtomwriteError::SymlinkBlocked { .. } => {
Some(t!("suggestion.symlink-blocked").to_string())
}
AtomwriteError::FileImmutable { path } => Some(
t!(
"suggestion.immutable-path",
path = path.display().to_string()
)
.to_string(),
),
AtomwriteError::BinaryFile { .. } => Some(t!("suggestion.binary-file").to_string()),
AtomwriteError::FifoDetected { .. } => Some(t!("suggestion.skip-special").to_string()),
AtomwriteError::DeviceFile { .. } => Some(t!("suggestion.skip-special").to_string()),
AtomwriteError::NoMatches => Some(t!("suggestion.no-matches").to_string()),
AtomwriteError::BrokenPipe => None,
AtomwriteError::MatchAmbiguous { .. } => {
Some(t!("suggestion.match-ambiguous").to_string())
}
AtomwriteError::MatchFailed { .. } => Some(t!("suggestion.match-failed").to_string()),
AtomwriteError::Cancelled { .. } => Some(t!("suggestion.cancelled").to_string()),
AtomwriteError::InternalError { reason } => Some(
t!("suggestion.internal-error", reason = reason.as_str()).to_string(),
),
AtomwriteError::LockTimeout { path, timeout_ms } => Some(
t!(
"suggestion.lock-timeout",
path = path.display().to_string(),
timeout_ms = timeout_ms
)
.to_string(),
),
AtomwriteError::SyntaxError { path, count } => Some(
t!(
"suggestion.syntax-error",
path = path.display().to_string(),
count = count
)
.to_string(),
),
AtomwriteError::ExdevFallbackDisabled { path } => Some(
t!(
"suggestion.exdev-fallback-disabled",
path = path.display().to_string()
)
.to_string(),
),
AtomwriteError::CopyBackBlake3Failed { path } => Some(
t!(
"suggestion.copy-back-blake3-failed",
path = path.display().to_string()
)
.to_string(),
),
AtomwriteError::OrphanJournal { journal, reason } => Some(
t!(
"suggestion.orphan-journal",
journal = journal.display().to_string(),
reason = reason.as_str()
)
.to_string(),
),
}
}