use crate::commands::load_quill;
use crate::errors::{CliError, Result};
use crate::output::{derive_output_path, page_output_path, write_file, write_stdout};
use clap::Parser;
use quillmark::{OutputFormat, Quillmark, RenderOptions};
use std::fs;
use std::path::PathBuf;
#[derive(Parser)]
pub struct RenderArgs {
#[arg(value_name = "QUILL_PATH")]
quill: PathBuf,
#[arg(value_name = "MARKDOWN_FILE")]
markdown_file: Option<PathBuf>,
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
#[arg(short, long, value_name = "FORMAT", default_value = "pdf")]
format: String,
#[arg(long)]
stdout: bool,
#[arg(long)]
quiet: bool,
#[arg(long, value_name = "DATA_FILE")]
output_data: Option<PathBuf>,
}
pub fn execute(args: RenderArgs) -> Result<()> {
let quill = load_quill(&args.quill)?;
let (parsed, parse_warnings, markdown_path_for_output) =
if let Some(ref markdown_path) = args.markdown_file {
if !markdown_path.exists() {
return Err(CliError::InvalidArgument(format!(
"Markdown file not found: {}",
markdown_path.display()
)));
}
let markdown = fs::read_to_string(markdown_path)?;
let output = quill.parse(&markdown)?;
(output.document, output.warnings, Some(markdown_path.clone()))
} else {
(quill.seed_document(), Vec::new(), None)
};
let output_format = args
.format
.parse::<OutputFormat>()
.map_err(|e| CliError::InvalidArgument(e.to_string()))?;
if let Some(data_path) = args.output_data {
let json_data = quill.compile_data(&parsed).map_err(CliError::Render)?;
let f = std::fs::File::create(&data_path).map_err(|e| {
CliError::Io(std::io::Error::new(
e.kind(),
format!(
"Failed to create data output file '{}': {}",
data_path.display(),
e
),
))
})?;
serde_json::to_writer_pretty(f, &json_data).map_err(|e| {
CliError::Io(std::io::Error::other(format!(
"Failed to write JSON data: {}",
e
)))
})?;
}
let engine = Quillmark::new();
let mut result = engine.render(
&quill,
&parsed,
&RenderOptions::default().with_output_format(output_format),
)?;
result.warnings.splice(0..0, parse_warnings);
if !result.warnings.is_empty() && !args.quiet {
crate::errors::print_warnings(&result.warnings);
}
if result.artifacts.is_empty() {
return Err(CliError::InvalidArgument(
"No artifacts produced from rendering".to_string(),
));
}
if args.stdout {
if result.artifacts.len() > 1 {
return Err(CliError::InvalidArgument(format!(
"{} renders {} pages, one artifact each, and --stdout carries one; \
drop --stdout to write the pages as files",
output_format,
result.artifacts.len()
)));
}
write_stdout(&result.artifacts[0].bytes)?;
} else {
let output_path = args.output.unwrap_or_else(|| {
if let Some(ref path) = markdown_path_for_output {
derive_output_path(path, output_format.as_str())
} else {
PathBuf::from(format!("example.{}", output_format))
}
});
if let [artifact] = result.artifacts.as_slice() {
write_file(&output_path, &artifact.bytes, !args.quiet)?;
} else {
for (i, artifact) in result.artifacts.iter().enumerate() {
let path = page_output_path(&output_path, i + 1);
write_file(&path, &artifact.bytes, !args.quiet)?;
}
}
}
Ok(())
}