use crate::{CliError, CliResult, GlobalConfig, InputArgs};
use bumpalo::Bump;
use clap::Args;
use css_ast::{CssAtomSet, StyleSheet, Visitable};
use css_lexer::Lexer;
use css_parse::{CursorCompactWriteSink, CursorOverlaySink, Parser, ToCursors};
use csskit_highlight::{AnsiHighlightCursorStream, DefaultAnsiTheme, TokenHighlighter};
use csskit_transform::{CssMinifierFeature, Transformer};
use std::io::Read;
#[derive(Debug, Args)]
pub struct Min {
#[command(flatten)]
content: InputArgs,
#[arg(short, long, group = "output_file", value_parser)]
output: Option<String>,
#[arg(long, value_parser)]
check: bool,
}
impl Min {
pub fn run(&self, config: GlobalConfig) -> CliResult {
let Min { content, output, check } = self;
let color = config.colors() && output.is_none() && !*check;
let bump = Bump::default();
let start = std::time::Instant::now();
if *check && output.is_some() {
eprintln!("Ignoring output option, because check was passed");
}
let mut checks = 0;
for (file_name, mut source) in content.sources()? {
let mut source_string = String::new();
source.read_to_string(&mut source_string)?;
let source_text = source_string.as_str();
let lexer = Lexer::new(&CssAtomSet::ATOMS, source_text);
let mut parser = Parser::new(&bump, source_text, lexer);
let mut result = parser.parse_entirely::<StyleSheet>().with_trivia();
if let Some(ref mut stylesheet) = result.output {
let mut transformer =
Transformer::new_in(&bump, CssMinifierFeature::all_bits(), &CssAtomSet::ATOMS, source_text);
transformer.transform(stylesheet);
let overlays = transformer.overlays();
let mut str = String::new();
if color {
let mut highlighter = TokenHighlighter::new();
stylesheet.accept(&mut highlighter);
let ansi = AnsiHighlightCursorStream::new(&mut str, &highlighter, DefaultAnsiTheme);
{
let mut stream = CursorOverlaySink::new(
source_text,
&overlays,
CursorCompactWriteSink::new(source_text, ansi),
);
result.to_cursors(&mut stream);
}
} else {
{
let mut stream = CursorOverlaySink::new(
source_text,
&overlays,
CursorCompactWriteSink::new(source_text, &mut str),
);
result.to_cursors(&mut stream);
}
};
if *check {
if str != source_text {
println!("{str}");
checks += 1;
}
} else if let Some(file) = output {
std::fs::write(file, str.as_bytes())?;
} else {
println!("{str}");
}
} else {
for compact_err in result.errors {
let report = crate::commands::format_diagnostic_error(&compact_err, &source_string, file_name);
println!("{report}");
}
}
}
eprintln!("Slurped up CSS in {:?}! Neat!", start.elapsed());
if checks > 0 { Err(CliError::Checks(checks))? } else { Ok(()) }
}
}