pub mod html_to_pdf;
pub mod latex;
pub mod markdown;
pub mod text;
use crate::cli::{DocumentFormat, Theme};
use anyhow::Result;
use colored::*;
use indicatif::{ProgressBar, ProgressStyle};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::Instant;
pub fn detect_format_from_path(path: &Path) -> DocumentFormat {
match path.extension().and_then(|e| e.to_str()).map(|e| e.to_lowercase()).as_deref() {
Some("md") | Some("markdown") | Some("mdown") => DocumentFormat::Markdown,
Some("tex") | Some("latex") | Some("ltx") => DocumentFormat::Latex,
_ => DocumentFormat::PlainText,
}
}
pub fn determine_output_path(input_path: &Path, explicit_output: Option<PathBuf>) -> PathBuf {
if let Some(out) = explicit_output {
return out;
}
let stem = input_path
.file_stem()
.map(|s| s.to_string_lossy())
.unwrap_or_else(|| "document".into());
let parent = input_path.parent().unwrap_or_else(|| Path::new("."));
parent.join(format!("{}.pdf", stem))
}
pub fn compile(
input_path: &Path,
output_path: &Path,
format: DocumentFormat,
theme: Theme,
) -> Result<()> {
let start_time = Instant::now();
let spinner = ProgressBar::new_spinner();
spinner.set_style(
ProgressStyle::default_spinner()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏")
.template("{spinner:.green} {msg}")
.unwrap(),
);
let file_display = input_path.display().to_string();
spinner.set_message(format!(
"Compiling {} as {:?}...",
file_display.bold(),
format
));
spinner.enable_steady_tick(std::time::Duration::from_millis(80));
let compile_result = match format {
DocumentFormat::Markdown => markdown::compile_markdown(input_path, output_path, theme),
DocumentFormat::Latex => latex::compile_latex(input_path, output_path),
DocumentFormat::PlainText => text::compile_plain_text(input_path, output_path, theme),
};
match compile_result {
Ok(()) => {
spinner.finish_and_clear();
let elapsed = start_time.elapsed();
let file_size_str = fs::metadata(output_path)
.map(|m| format_file_size(m.len()))
.unwrap_or_else(|_| "unknown size".into());
println!(
"{} Generated: {} ({}, {:.2}s)",
"✔".bright_green().bold(),
output_path.display().to_string().bright_cyan().bold(),
file_size_str.dimmed(),
elapsed.as_secs_f64()
);
Ok(())
}
Err(e) => {
spinner.finish_and_clear();
eprintln!("{} Compilation failed!", "✖".bright_red().bold());
Err(e)
}
}
}
fn format_file_size(bytes: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
if bytes >= MB {
format!("{:.1} MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.1} KB", bytes as f64 / KB as f64)
} else {
format!("{} B", bytes)
}
}