use anyhow::{Context as _, Result};
use clap::{Args, ValueEnum};
use std::io::Read;
use camino::Utf8PathBuf;
use crate::{
util::{split_recipe_name_and_scaling_factor, write_to_output, PARSER},
Context,
};
use cooklang_find::RecipeEntry;
#[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,
}
#[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 mut scale = args.input.scale;
let (recipe, title) = if let Some(query) = args.input.recipe {
let (name, scaling_factor) = split_recipe_name_and_scaling_factor(query.as_str())
.map(|(name, factor)| (name, Some(factor)))
.unwrap_or((query.as_str(), None));
if let Some(scaling_factor) = scaling_factor {
scale = scaling_factor;
}
let recipe_entry = cooklang_find::get_recipe(vec![ctx.base_path().clone()], name.into())
.map_err(|e| anyhow::anyhow!("Recipe not found: {}", e))?;
let recipe = crate::util::parse_recipe_from_entry(&recipe_entry, scale)?;
(recipe, recipe_entry.name().clone().unwrap_or(String::new()))
} else {
let mut buf = String::new();
std::io::stdin()
.read_to_string(&mut buf)
.context("Failed to read stdin")?;
let recipe_entry = RecipeEntry::from_content(buf, Some("stdin".to_string()))
.context("Failed to create recipe entry from stdin")?;
let recipe = crate::util::parse_recipe_from_entry(&recipe_entry, scale)?;
(recipe, recipe_entry.name().clone().unwrap_or(String::new()))
};
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,
});
write_to_output(args.output.as_deref(), |writer| {
match format {
OutputFormat::Human => crate::util::cooklang_to_human::print_human(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
)?,
OutputFormat::Json => {
if args.pretty {
serde_json::to_writer_pretty(writer, &recipe)?;
} else {
serde_json::to_writer(writer, &recipe)?;
}
}
OutputFormat::Cooklang => {
crate::util::cooklang_to_cooklang::print_cooklang(&recipe, writer)?
}
OutputFormat::Yaml => serde_yaml::to_writer(writer, &recipe)?,
OutputFormat::Markdown => crate::util::cooklang_to_md::print_md(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
)?,
OutputFormat::Latex => crate::util::cooklang_to_latex::print_latex(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
)?,
OutputFormat::Typst => crate::util::cooklang_to_typst::print_typst(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
)?,
OutputFormat::Schema => crate::util::cooklang_to_schema::print_schema(
&recipe,
&title,
scale,
PARSER.converter(),
writer,
args.pretty,
)?,
}
Ok(())
})?;
Ok(())
}