use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use clio::Input;
use derive_more::{Display, Error, From};
use hugr::extension::ExtensionRegistry;
use hugr::package::{PackageEncodingError, PackageValidationError};
use hugr::Hugr;
use std::io::{Cursor, Read, Seek, SeekFrom};
use std::{ffi::OsString, path::PathBuf};
pub mod extensions;
pub mod mermaid;
pub mod validate;
use hugr::package::Package;
#[derive(Parser, Debug)]
#[clap(version = "1.0", long_about = None)]
#[clap(about = "HUGR CLI tools.")]
#[group(id = "hugr")]
#[non_exhaustive]
pub enum CliArgs {
Validate(validate::ValArgs),
GenExtensions(extensions::ExtArgs),
Mermaid(mermaid::MermaidArgs),
#[command(external_subcommand)]
External(Vec<OsString>),
}
#[derive(Debug, Display, Error, From)]
#[non_exhaustive]
pub enum CliError {
#[display("Error reading from path: {_0}")]
InputFile(std::io::Error),
#[display("Error parsing package: {_0}")]
Parse(serde_json::Error),
#[display("Error parsing package: {_0}")]
HUGRLoad(PackageEncodingError),
#[display("Error validating HUGR: {_0}")]
Validate(PackageValidationError),
}
#[derive(Parser, Debug)]
pub struct HugrArgs {
#[clap(value_parser, default_value = "-")]
pub input: Input,
#[command(flatten)]
pub verbose: Verbosity<InfoLevel>,
#[arg(
long,
help = "Don't use standard extensions when validating. Prelude is still used."
)]
pub no_std: bool,
#[arg(
short,
long,
help = "Paths to serialised extensions to validate against."
)]
pub extensions: Vec<PathBuf>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PackageOrHugr {
Package(Package),
Hugr(Hugr),
}
impl PackageOrHugr {
pub fn into_hugrs(self) -> Vec<Hugr> {
match self {
PackageOrHugr::Package(pkg) => pkg.modules,
PackageOrHugr::Hugr(hugr) => vec![hugr],
}
}
pub fn validate(&self) -> Result<(), PackageValidationError> {
match self {
PackageOrHugr::Package(pkg) => pkg.validate(),
PackageOrHugr::Hugr(hugr) => Ok(hugr.validate()?),
}
}
}
impl AsRef<[Hugr]> for PackageOrHugr {
fn as_ref(&self) -> &[Hugr] {
match self {
PackageOrHugr::Package(pkg) => &pkg.modules,
PackageOrHugr::Hugr(hugr) => std::slice::from_ref(hugr),
}
}
}
impl HugrArgs {
pub fn get_package_or_hugr(
&mut self,
extensions: &ExtensionRegistry,
) -> Result<PackageOrHugr, CliError> {
match self.input.can_seek() {
true => get_package_or_hugr_seek(&mut self.input, extensions),
false => {
let mut buffer = Vec::new();
self.input.read_to_end(&mut buffer)?;
get_package_or_hugr_seek(Cursor::new(buffer), extensions)
}
}
}
}
fn get_package_or_hugr_seek<I: Seek + Read>(
mut input: I,
extensions: &ExtensionRegistry,
) -> Result<PackageOrHugr, CliError> {
match Hugr::load_json(&mut input, extensions) {
Ok(hugr) => Ok(PackageOrHugr::Hugr(hugr)),
Err(_) => {
input.seek(SeekFrom::Start(0))?;
let pkg = Package::from_json_reader(input, extensions)?;
Ok(PackageOrHugr::Package(pkg))
}
}
}