use std::{
fs,
path::{Path, PathBuf},
time::Instant,
};
use clap::ArgMatches;
use hsml::compile_content_diagnostics_with_options;
use super::common::{resolve_ignore_patterns, resolve_path, validate_hsml_extension};
use super::diagnostics::{
FileDiagnostics, format_duration, has_errors, print_summary, render_diagnostics, resolve_colors,
};
use super::walker::walk_hsml_files;
pub fn exec_compile(matches: &ArgMatches) -> Result<(), String> {
let out = matches.get_one::<PathBuf>("output");
let format = matches
.get_one::<String>("report_format")
.map(|s| s.as_str());
let debug = matches.get_flag("debug");
let pretty = matches.get_flag("pretty");
let (no_color, dim) = resolve_colors(matches);
let ignore_patterns = resolve_ignore_patterns(matches);
let path = resolve_path(matches)?;
let path = &path;
let mut diagnostics: Vec<FileDiagnostics> = Vec::new();
let mut io_errors: Vec<String> = Vec::new();
let mut file_count: usize = 0;
let total_start = Instant::now();
if path.is_dir() {
match walk_hsml_files(path, &ignore_patterns) {
Ok(result) => {
if debug {
println!(
"{}Compiling {} file(s) from {}{}",
dim.0,
result.files.len(),
path.display(),
dim.1
);
}
io_errors.extend(result.errors);
file_count = result.files.len();
for file in &result.files {
if let Err(e) = compile_file(
file,
None,
pretty,
debug,
dim,
&mut diagnostics,
&mut io_errors,
) {
io_errors.push(e);
}
}
}
Err(e) => io_errors.push(e),
}
} else if path.is_file() {
file_count = 1;
if let Err(e) = compile_file(
path,
out,
pretty,
debug,
dim,
&mut diagnostics,
&mut io_errors,
) {
io_errors.push(e);
}
} else {
return Err("Path must be a file or directory".to_string());
}
render_diagnostics(&diagnostics, format, no_color);
if debug {
print_summary(
&diagnostics,
file_count,
io_errors.len(),
total_start.elapsed(),
no_color,
"compiled",
);
}
if !io_errors.is_empty() {
Err(io_errors.join("\n"))
} else if has_errors(&diagnostics) {
Err(String::new())
} else {
Ok(())
}
}
fn compile_file(
file: &Path,
out_file: Option<&PathBuf>,
pretty: bool,
debug: bool,
dim: (&str, &str),
diagnostics: &mut Vec<FileDiagnostics>,
io_errors: &mut Vec<String>,
) -> Result<(), String> {
if !file.exists() {
return Err("File does not exist".to_string());
}
if !file.is_file() {
return Err("Given file must be a file".to_string());
}
validate_hsml_extension(file)?;
let content = fs::read_to_string(file)
.map_err(|e| format!("Unable to read file {}: {e}", file.display()))?;
let fallback_out_file = file.with_extension("html");
let out_file = out_file.unwrap_or(&fallback_out_file);
let start = Instant::now();
let compile_options = hsml::compiler::HsmlCompileOptions {
pretty,
..Default::default()
};
match compile_content_diagnostics_with_options(&content, &compile_options) {
Ok(output) => {
if let Err(e) = fs::write(out_file, &output.html) {
io_errors.push(format!("Unable to write file {}: {e}", out_file.display()));
} else if debug {
let timing = format_duration(start.elapsed());
println!("{}{} {timing}{}", dim.0, out_file.display(), dim.1);
}
let file_diags: Vec<_> = output
.diagnostics
.into_iter()
.map(|d| d.with_file_path(file.display().to_string()))
.collect();
if !file_diags.is_empty() {
diagnostics.push(FileDiagnostics {
diagnostics: file_diags,
source: content,
});
}
}
Err(errs) => {
let file_diags: Vec<_> = errs
.into_iter()
.map(|d| d.with_file_path(file.display().to_string()))
.collect();
diagnostics.push(FileDiagnostics {
diagnostics: file_diags,
source: content,
});
}
}
Ok(())
}