use std::path::Path;
use crate::containment::PathGuard;
use crate::plan::Operation;
use crate::write::EolMode;
use super::{ApplyMode, EditResult, WritePolicyOptions};
#[derive(Debug, Clone, Default)]
pub struct TidyIndentOptions {
pub dedent: Option<String>,
pub indent: Option<String>,
pub lines: Option<String>,
}
pub fn tidy(
path: &Path,
policy_opts: &WritePolicyOptions,
mode: ApplyMode,
guard: Option<&PathGuard>,
) -> anyhow::Result<EditResult> {
tidy_with_indent(
path,
policy_opts,
&TidyIndentOptions::default(),
mode,
guard,
)
}
pub fn tidy_with_indent(
path: &Path,
policy_opts: &WritePolicyOptions,
indent_opts: &TidyIndentOptions,
mode: ApplyMode,
guard: Option<&PathGuard>,
) -> anyhow::Result<EditResult> {
let eol_str = policy_opts.normalize_eol.map(|eol| match eol {
EolMode::Lf => "lf".to_string(),
EolMode::Crlf => "crlf".to_string(),
EolMode::Cr => "cr".to_string(),
EolMode::Keep => "keep".to_string(),
});
let op = Operation::TidyFix {
path: path.to_string_lossy().into(),
ensure_final_newline: Some(policy_opts.ensure_final_newline),
trim_trailing_whitespace: Some(policy_opts.trim_trailing_whitespace),
normalize_eol: eol_str,
collapse_blanks: if policy_opts.collapse_blanks {
Some(true)
} else {
None
},
dedent: indent_opts.dedent.clone(),
indent: indent_opts.indent.clone(),
lines: indent_opts.lines.clone(),
};
tidy_write(op, path, mode, guard)
}
#[cfg(any(feature = "cli", feature = "files"))]
fn tidy_write(
op: Operation,
path: &Path,
mode: ApplyMode,
guard: Option<&PathGuard>,
) -> anyhow::Result<EditResult> {
let cwd = path.parent().unwrap_or_else(|| Path::new("."));
super::execute_as_edit_result(op, mode, cwd, guard, "tidy", None)
}
#[cfg(not(any(feature = "cli", feature = "files")))]
fn tidy_write(
_op: Operation,
path: &Path,
mode: ApplyMode,
guard: Option<&PathGuard>,
) -> anyhow::Result<EditResult> {
use anyhow::Context;
if let Operation::TidyFix {
ensure_final_newline,
trim_trailing_whitespace,
normalize_eol,
collapse_blanks,
dedent,
indent,
lines,
..
} = _op
{
let path_str = path.to_string_lossy();
let original = std::fs::read_to_string(path)
.with_context(|| format!("failed to read {}", path.display()))?;
let eol = normalize_eol
.as_deref()
.map(|s| match s {
"lf" => EolMode::Lf,
"crlf" => EolMode::Crlf,
"cr" => EolMode::Cr,
_ => EolMode::Keep,
})
.unwrap_or(EolMode::Keep);
let policy = crate::write::WritePolicy {
ensure_final_newline: ensure_final_newline.unwrap_or(false),
normalize_eol: eol,
trim_trailing_whitespace: trim_trailing_whitespace.unwrap_or(false),
collapse_blanks: collapse_blanks.unwrap_or(false),
};
let mut new_content = crate::write::apply_policy(&original, &policy).into_owned();
let line_range = lines
.as_deref()
.map(crate::ops::read::parse_line_range)
.transpose()?;
if let Some(ref spec) = dedent {
new_content = crate::write::dedent_content(&new_content, spec, line_range);
}
if let Some(ref spec) = indent {
new_content = crate::write::indent_content(&new_content, spec, line_range);
}
let noop_policy = crate::write::WritePolicy::default();
let applied = super::write_if_apply(path, &new_content, mode, &noop_policy, guard)?;
Ok(super::build_edit_result(
&path_str,
original,
new_content,
applied,
"tidy",
None,
))
} else {
anyhow::bail!("expected TidyFix operation")
}
}