use anyhow::{Context as _, Result};
use clap::{Args, ValueEnum};
use std::io::Read;
use camino::Utf8PathBuf;
use crate::{
util::{
format::{self, Style},
split_recipe_name_and_scaling_factor, write_to_output, PARSER,
},
Context,
};
#[derive(Debug, Args)]
pub struct ReadArgs {
#[command(flatten)]
input: super::RecipeInputArgs,
#[arg(short, long, value_hint = clap::ValueHint::FilePath)]
output: Option<Utf8PathBuf>,
#[arg(short, long, value_enum)]
format: Option<OutputFormat>,
#[arg(long)]
pretty: bool,
#[arg(short = 'p', long, value_enum)]
paper_size: Option<PaperSizeArg>,
#[arg(short, long)]
margin: Option<f64>,
}
#[derive(Debug, Clone, Copy, ValueEnum)]
enum PaperSizeArg {
A4,
Letter,
A5,
Legal,
}
impl From<PaperSizeArg> for format::PaperSize {
fn from(value: PaperSizeArg) -> Self {
match value {
PaperSizeArg::A4 => format::PaperSize::A4,
PaperSizeArg::Letter => format::PaperSize::Letter,
PaperSizeArg::A5 => format::PaperSize::A5,
PaperSizeArg::Legal => format::PaperSize::Legal,
}
}
}
#[derive(Debug, Clone, Copy, ValueEnum)]
enum OutputFormat {
Human,
Json,
#[value(alias("yml"))]
Yaml,
#[value(alias("cook"))]
Cooklang,
#[value(alias("md"))]
Markdown,
#[value(alias("tex"))]
Latex,
#[value(alias("typ"))]
Typst,
#[value(alias("jsonld"))]
Schema,
}
pub fn run(ctx: &Context, args: ReadArgs) -> Result<()> {
let (source, scale) = match args.input.recipe {
Some(query) => match split_recipe_name_and_scaling_factor(query.as_str()) {
Some((name, factor)) => (
cookcli_core::RecipeSource::Path(Utf8PathBuf::from(name)),
factor,
),
None => (cookcli_core::RecipeSource::Path(query), args.input.scale),
},
None => {
let mut buf = String::new();
std::io::stdin()
.read_to_string(&mut buf)
.context("Failed to read stdin")?;
(
cookcli_core::RecipeSource::Content {
text: buf,
name: "stdin".to_string(),
},
args.input.scale,
)
}
};
let outcome = cookcli_core::recipe::read(
&cookcli_core::Context::new(ctx.base_path().to_path_buf()),
cookcli_core::recipe::ReadRequest { source, scale },
)
.map_err(crate::util::cli_error)?;
let recipe = outcome.value.recipe;
let title = outcome.value.title;
for diagnostic in &outcome.diagnostics {
tracing::warn!("Recipe '{}': {}", title, diagnostic.message);
}
let format = args.format.unwrap_or_else(|| match &args.output {
Some(p) => match p.extension() {
Some("json") => OutputFormat::Json,
Some("cook") => OutputFormat::Cooklang,
Some("md") => OutputFormat::Markdown,
Some("yaml") => OutputFormat::Yaml,
Some("yml") => OutputFormat::Yaml,
Some("tex") => OutputFormat::Latex,
Some("latex") => OutputFormat::Latex,
Some("typ") => OutputFormat::Typst,
Some("jsonld") => OutputFormat::Schema,
_ => OutputFormat::Human,
},
None => OutputFormat::Human,
});
if !matches!(format, OutputFormat::Latex | OutputFormat::Typst) {
if args.paper_size.is_some() {
eprintln!("warning: --paper-size has no effect with the selected output format");
}
if args.margin.is_some() {
eprintln!("warning: --margin has no effect with the selected output format");
}
}
let paper_size: format::PaperSize = args.paper_size.unwrap_or(PaperSizeArg::A4).into();
let margin = args.margin.unwrap_or(2.5);
write_to_output(args.output.as_deref(), |mut writer| {
match format {
OutputFormat::Human => format::human::print_human(
&recipe,
&title,
scale,
PARSER.converter(),
Style::Ansi,
&mut writer,
)?,
OutputFormat::Json => {
if args.pretty {
serde_json::to_writer_pretty(writer, &recipe)?;
} else {
serde_json::to_writer(writer, &recipe)?;
}
}
OutputFormat::Cooklang => format::cooklang::print_cooklang(&recipe, writer)?,
OutputFormat::Yaml => serde_yaml::to_writer(writer, &recipe)?,
OutputFormat::Markdown => {
format::markdown::print_md(&recipe, &title, scale, PARSER.converter(), writer)?
}
OutputFormat::Latex => format::latex::print_latex(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
paper_size,
margin,
)?,
OutputFormat::Typst => format::typst::print_typst(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
paper_size,
margin,
)?,
OutputFormat::Schema => format::schema::print_schema(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
args.pretty,
)?,
}
Ok(())
})?;
Ok(())
}