pub mod biome;
pub mod builtin_filters;
pub mod bun;
pub mod cargo;
pub mod eslint;
pub mod generic;
pub mod git;
pub mod npm;
pub mod pnpm;
pub mod pytest;
pub mod toml_filter;
pub mod trust;
pub mod tsc;
pub mod vitest;
use crate::context::AppContext;
use biome::BiomeCompressor;
use bun::BunCompressor;
use cargo::CargoCompressor;
use eslint::EslintCompressor;
use generic::{strip_ansi, GenericCompressor};
use git::GitCompressor;
use npm::NpmCompressor;
use pnpm::PnpmCompressor;
use pytest::PytestCompressor;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use toml_filter::{apply_filter, FilterRegistry};
use tsc::TscCompressor;
use vitest::VitestCompressor;
pub type SharedFilterRegistry = Arc<RwLock<FilterRegistry>>;
pub trait Compressor {
fn matches(&self, command: &str) -> bool;
fn compress(&self, command: &str, output: &str) -> String;
}
pub fn compress(command: &str, output: String, ctx: &AppContext) -> String {
if !ctx.config().experimental_bash_compress {
return output;
}
let registry_handle = ctx.shared_filter_registry();
let guard = match registry_handle.read() {
Ok(g) => g,
Err(poisoned) => poisoned.into_inner(),
};
compress_with_registry(command, &output, &guard)
}
pub fn compress_with_registry(command: &str, output: &str, registry: &FilterRegistry) -> String {
let stripped_for_generic = strip_ansi(output);
let compressors: [&dyn Compressor; 10] = [
&GitCompressor,
&CargoCompressor,
&TscCompressor,
&NpmCompressor,
&BunCompressor,
&PnpmCompressor,
&PytestCompressor,
&EslintCompressor,
&VitestCompressor,
&BiomeCompressor,
];
for compressor in compressors {
if compressor.matches(command) {
return compressor.compress(command, &stripped_for_generic);
}
}
if let Some(filter) = registry.lookup(command) {
return apply_filter(filter, output);
}
GenericCompressor.compress(command, &stripped_for_generic)
}
pub fn build_registry_for_context(ctx: &AppContext) -> FilterRegistry {
let config = ctx.config();
let storage_dir = config.storage_dir.clone();
let project_root = config.project_root.clone();
drop(config);
let user_dir = storage_dir.as_ref().map(|d| d.join("filters"));
let project_dir = match (project_root.as_ref(), storage_dir.as_ref()) {
(Some(root), Some(storage)) => {
if trust::is_project_trusted(Some(storage), root) {
Some(root.join(".aft").join("filters"))
} else {
None
}
}
_ => None,
};
toml_filter::build_registry(
builtin_filters::ALL,
user_dir.as_deref(),
project_dir.as_deref(),
)
}
pub fn user_filter_dir(storage_dir: &Path) -> PathBuf {
storage_dir.join("filters")
}
pub fn project_filter_dir(project_root: &Path) -> PathBuf {
project_root.join(".aft").join("filters")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_and_project_filter_dir_helpers() {
let storage = Path::new("/tmp/aft-storage");
assert_eq!(
user_filter_dir(storage),
Path::new("/tmp/aft-storage/filters")
);
let project = Path::new("/repo");
assert_eq!(project_filter_dir(project), Path::new("/repo/.aft/filters"));
}
}