use clap::{Parser, Subcommand};
use neopdf::converter;
use neopdf::metadata::{InterpolatorType, SetType};
use std::fs::File;
use std::io::{BufRead, BufReader};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
Convert {
#[arg(short, long)]
pdf_name: String,
#[arg(short, long)]
output: String,
},
CombineNpdfs {
#[arg(
short = 'n',
long = "pdf-names",
required_unless_present = "names_file"
)]
pdf_names: Option<Vec<String>>,
#[arg(short = 'f', long = "names-file", conflicts_with = "pdf_names")]
names_file: Option<String>,
#[arg(short, long)]
output: String,
},
CombineAlphas {
#[arg(
short = 'n',
long = "pdf-names",
required_unless_present = "names_file"
)]
pdf_names: Option<Vec<String>>,
#[arg(short = 'f', long = "names-file", conflicts_with = "pdf_names")]
names_file: Option<String>,
#[arg(short, long)]
output: String,
},
Metadata {
#[arg(short, long)]
path: String,
#[arg(short, long)]
key: String,
#[arg(short, long)]
value: String,
},
}
fn load_pdf_names(
pdf_names: Option<&[String]>,
names_file: Option<&str>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
match (pdf_names, names_file) {
(Some(names), None) => Ok(names.to_vec()),
(None, Some(file_path)) => {
let file = File::open(file_path)?;
BufReader::new(file)
.lines()
.collect::<Result<Vec<_>, _>>()
.map_err(Into::into)
}
_ => Err("Either --pdf-names or --names-file must be provided.".into()),
}
}
#[allow(clippy::needless_pass_by_value)]
pub fn run_cli(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
match &cli.command {
Commands::Convert { pdf_name, output } => {
converter::convert_lhapdf(pdf_name, output)?;
}
Commands::CombineNpdfs {
pdf_names,
names_file,
output,
} => {
let names = load_pdf_names(pdf_names.as_deref(), names_file.as_deref())?;
let names_str: Vec<&str> = names.iter().map(String::as_str).collect();
converter::combine_lhapdf_npdfs(&names_str, output)?;
}
Commands::CombineAlphas {
pdf_names,
names_file,
output,
} => {
let names = load_pdf_names(pdf_names.as_deref(), names_file.as_deref())?;
let names_str: Vec<&str> = names.iter().map(String::as_str).collect();
converter::combine_lhapdf_alphas(&names_str, output)?;
}
Commands::Metadata { path, key, value } => {
let grids_with_metadata = neopdf::writer::GridArrayCollection::decompress(path)?;
let mut metadata = grids_with_metadata[0].metadata.as_ref().clone();
match key.as_str() {
"SetDesc" => metadata.set_desc = value.to_string(),
"SetIndex" => metadata.set_index = value.parse()?,
"NumMembers" => metadata.num_members = value.parse()?,
"XMin" => metadata.x_min = value.parse()?,
"XMax" => metadata.x_max = value.parse()?,
"QMin" => metadata.q_min = value.parse()?,
"QMax" => metadata.q_max = value.parse()?,
"Format" => metadata.format = value.to_string(),
"Polarized" => metadata.polarised = value.parse()?,
"InterpolatorType" => {
metadata.interpolator_type = match value.to_lowercase().as_str() {
"bilinear" => InterpolatorType::Bilinear,
"logbilinear" => InterpolatorType::LogBilinear,
"logbicubic" => InterpolatorType::LogBicubic,
"logtricubic" => InterpolatorType::LogTricubic,
"interpndlinear" => InterpolatorType::InterpNDLinear,
_ => return Err(format!("Unknown InterpolatorType: {value}").into()),
}
}
"ErrorType" => metadata.error_type = value.to_string(),
"Particle" => metadata.hadron_pid = value.parse()?,
"GitVersion" => metadata.git_version = value.to_string(),
"CodeVersion" => metadata.code_version = value.to_string(),
"FlavorScheme" => metadata.flavor_scheme = value.to_string(),
"OrderQCD" => metadata.order_qcd = value.parse()?,
"AlphaS_OrderQCD" => metadata.alphas_order_qcd = value.parse()?,
"MW" => metadata.m_w = value.parse()?,
"MZ" => metadata.m_z = value.parse()?,
"MUp" => metadata.m_up = value.parse()?,
"MDown" => metadata.m_down = value.parse()?,
"MStrange" => metadata.m_strange = value.parse()?,
"MCharm" => metadata.m_charm = value.parse()?,
"MBottom" => metadata.m_bottom = value.parse()?,
"MTop" => metadata.m_top = value.parse()?,
"AlphaS_Type" => metadata.alphas_type = value.to_string(),
"NumFlavors" => metadata.number_flavors = value.parse()?,
"SetType" => {
metadata.set_type = match value.to_lowercase().as_str() {
"spacelike" => SetType::SpaceLike,
"timelike" => SetType::TimeLike,
_ => return Err(format!("Unknown SetType: {value}").into()),
}
}
_ => return Err(format!("Unknown metadata key: {key}").into()),
}
let grids_data: Vec<_> = grids_with_metadata.iter().map(|g| &g.grid).collect();
neopdf::writer::GridArrayCollection::compress(&grids_data, &metadata, path)?;
}
}
Ok(())
}
pub fn main(cli: Cli) {
if let Err(err) = run_cli(cli) {
eprintln!("Error: {err}");
std::process::exit(1);
}
}