use std::path::PathBuf;
use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use quilldown::{ConvertOptions, Converter, Margins, Orientation, PageSetup, PageSize, Theme};
#[derive(Debug, Clone, Copy, ValueEnum)]
enum PageSizeArg {
Letter,
A4,
Legal,
}
impl From<PageSizeArg> for PageSize {
fn from(a: PageSizeArg) -> Self {
match a {
PageSizeArg::Letter => PageSize::Letter,
PageSizeArg::A4 => PageSize::A4,
PageSizeArg::Legal => PageSize::Legal,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum)]
enum OrientationArg {
Portrait,
Landscape,
}
impl From<OrientationArg> for Orientation {
fn from(a: OrientationArg) -> Self {
match a {
OrientationArg::Portrait => Orientation::Portrait,
OrientationArg::Landscape => Orientation::Landscape,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum)]
enum ThemeArg {
Default,
Github,
Solarized,
}
impl From<ThemeArg> for Theme {
fn from(a: ThemeArg) -> Self {
match a {
ThemeArg::Default => Theme::DEFAULT,
ThemeArg::Github => Theme::GITHUB,
ThemeArg::Solarized => Theme::SOLARIZED,
}
}
}
#[derive(Debug, Parser)]
#[command(name = "quilldown", version, about)]
struct Cli {
input: PathBuf,
#[arg(short, long)]
output: Option<PathBuf>,
#[arg(long, default_value_t = 192.0)]
dpi: f32,
#[arg(long)]
base_dir: Option<PathBuf>,
#[arg(long)]
embed_svg: bool,
#[arg(long)]
svg_light_mode: bool,
#[arg(long)]
no_highlight: bool,
#[arg(long, value_enum, default_value_t = PageSizeArg::Letter)]
page_size: PageSizeArg,
#[arg(long, value_enum, default_value_t = OrientationArg::Portrait)]
orientation: OrientationArg,
#[arg(long, default_value_t = 1.0)]
margin: f32,
#[arg(long, value_enum, default_value_t = ThemeArg::Default)]
theme: ThemeArg,
#[arg(long)]
page_numbers: bool,
#[arg(long)]
toc: bool,
#[arg(long, default_value = "en-US")]
language: String,
#[arg(long)]
allow_remote_images: bool,
#[arg(long)]
captions: bool,
#[arg(short, long)]
verbose: bool,
}
fn main() -> Result<()> {
let cli = Cli::parse();
let output = cli.output.clone().unwrap_or_else(|| {
let mut o = cli.input.clone();
o.set_extension("docx");
o
});
let margin_dxa = (cli.margin.max(0.0) * 1440.0).round() as u32;
let page = PageSetup {
size: cli.page_size.into(),
orientation: cli.orientation.into(),
margins: Margins::uniform(margin_dxa),
};
let opts = ConvertOptions {
image_dpi: cli.dpi,
embed_svg: cli.embed_svg,
svg_light_mode: cli.svg_light_mode,
highlight_code: !cli.no_highlight,
base_dir: cli.base_dir.clone(),
page,
theme: cli.theme.into(),
page_numbers: cli.page_numbers,
table_of_contents: cli.toc,
language: cli.language.clone(),
allow_remote_images: cli.allow_remote_images,
captions: cli.captions,
..ConvertOptions::default()
};
let converter = Converter::new(opts);
let stats = converter
.convert_file(&cli.input, &output)
.with_context(|| format!("converting {} -> {}", cli.input.display(), output.display()))?;
if cli.verbose {
println!("{}", stats.summary());
}
for warning in &stats.warnings {
eprintln!("warning: {warning}");
}
println!("Wrote {}", output.display());
Ok(())
}