use crate::{
LintLevel,
context::{ExternalCmdFixData, LintContext},
rule::{DetectFix, Rule},
violation::{Detection, Fix, Replacement},
};
const NOTE: &str = "Use 'open' to read files as structured data, or 'open --raw' for plain text. \
While bat provides syntax highlighting, Nu's open auto-detects file formats \
(JSON, TOML, CSV, etc.) and parses them into structured tables.";
const STRUCTURED_EXTENSIONS: &[&str] = &[
".json", ".toml", ".yaml", ".yml", ".csv", ".tsv", ".xml", ".nuon", ".ini", ".ics", ".eml",
".vcf", ".xlsx", ".xls", ".ods", ".db", ".sqlite",
];
fn is_structured_file(filename: &str) -> bool {
let lower = filename.to_lowercase();
STRUCTURED_EXTENSIONS.iter().any(|ext| lower.ends_with(ext))
}
struct UseBuiltinBat;
impl DetectFix for UseBuiltinBat {
type FixInput<'a> = ExternalCmdFixData<'a>;
fn id(&self) -> &'static str {
"bat_to_open"
}
fn short_description(&self) -> &'static str {
"`bat` replaceable with `open` for file viewing"
}
fn source_link(&self) -> Option<&'static str> {
Some("https://www.nushell.sh/commands/docs/open.html")
}
fn level(&self) -> LintLevel {
LintLevel::Warning
}
fn detect<'a>(&self, context: &'a LintContext) -> Vec<(Detection, Self::FixInput<'a>)> {
let mut violations = context.detect_external_with_validation("bat", |_, _, _| Some(NOTE));
violations.extend(context.detect_external_with_validation("batcat", |_, _, _| Some(NOTE)));
violations
}
fn fix(&self, context: &LintContext, fix_data: &Self::FixInput<'_>) -> Option<Fix> {
let arg_texts: Vec<&str> = fix_data.arg_texts(context).collect();
let has_complex_flags = arg_texts.iter().any(|s| {
matches!(
*s,
"--language" | "-l" | "--theme" | "--style" | "--paging" | "--color"
) || s.starts_with("--language=")
|| s.starts_with("--theme=")
|| s.starts_with("--style=")
});
if has_complex_flags {
return None;
}
let filename = arg_texts.iter().find(|text| !text.starts_with('-'));
let (replacement, description) = filename.map_or_else(
|| {
(
"open --raw".to_string(),
"Use 'open --raw' for plain text files. For structured files (JSON, TOML, \
CSV), use 'open' without --raw to get parsed data."
.to_string(),
)
},
|file_text| {
if is_structured_file(file_text) {
(
format!("open {file_text}"),
format!(
"Use 'open {file_text}' to auto-parse this structured file. Nu will \
detect the format and return a table/record you can query directly."
),
)
} else {
(
format!("open --raw {file_text}"),
"Use 'open --raw' for plain text files. For structured files (JSON, TOML, \
CSV), use 'open' without --raw to get parsed data."
.to_string(),
)
}
},
);
Some(Fix {
explanation: description.into(),
replacements: vec![Replacement {
span: fix_data.expr_span.into(),
replacement_text: replacement.into(),
}],
})
}
}
pub static RULE: &dyn Rule = &UseBuiltinBat;
#[cfg(test)]
mod detect_bad;
#[cfg(test)]
mod generated_fix;
#[cfg(test)]
mod ignore_good;