use clio::Input;
use hugr::Extension;
use hugr::envelope::description::PackageDesc;
use hugr::envelope::read_envelope;
use hugr::extension::ExtensionRegistry;
use hugr::extension::resolution::WeakExtensionRegistry;
use hugr::package::Package;
use std::io::{BufReader, Read};
use std::path::PathBuf;
use walkdir::WalkDir;
use crate::CliError;
#[derive(Debug, clap::Args)]
pub struct HugrInputArgs {
#[arg(value_parser, default_value = "-", help_heading = "Input")]
pub input: Input,
#[arg(
long,
help_heading = "Input",
help = "Don't use standard extensions when validating hugrs. Prelude is still used."
)]
pub no_std: bool,
#[arg(
short,
long,
help_heading = "Input",
help = "Paths to additional serialised extensions needed to load the Hugr."
)]
pub extensions: Vec<PathBuf>,
#[arg(
short = 'E',
long,
help_heading = "Input",
help = "Directories to recursively search for serialised extensions (*.json) needed to load the Hugr. \
Files not matching the extension schema are ignored."
)]
pub extension_dirs: Vec<PathBuf>,
}
impl HugrInputArgs {
pub fn get_package(&mut self) -> Result<Package, CliError> {
self.get_described_package().map(|(_, package)| package)
}
pub fn get_described_package(&mut self) -> Result<(PackageDesc, Package), CliError> {
self.get_described_package_with_reader::<&[u8]>(None)
}
pub fn get_described_package_with_reader<R: Read>(
&mut self,
reader: Option<R>,
) -> Result<(PackageDesc, Package), CliError> {
let extensions = self.load_extensions()?;
match reader {
Some(r) => {
let buffer = BufReader::new(r);
Ok(read_envelope(buffer, &extensions)?)
}
None => {
let buffer = BufReader::new(&mut self.input);
Ok(read_envelope(buffer, &extensions)?)
}
}
}
pub fn load_extensions(&self) -> Result<ExtensionRegistry, CliError> {
let mut reg = if self.no_std {
hugr::extension::PRELUDE_REGISTRY.to_owned()
} else {
hugr::std_extensions::STD_REG.to_owned()
};
let mut extensions: Vec<Extension> = Vec::with_capacity(self.extensions.len());
for ext in &self.extensions {
let f = std::fs::File::open(ext)?;
let ext: Extension = serde_json::from_reader(f)?;
extensions.push(ext);
}
for dir in &self.extension_dirs {
for entry in WalkDir::new(dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
{
let f = std::fs::File::open(entry.path())?;
if let Ok(ext) = serde_json::from_reader::<_, Extension>(f) {
extensions.push(ext);
}
}
}
let extra_extensions = ExtensionRegistry::new_with_extension_resolution(
extensions,
&WeakExtensionRegistry::from(®),
)?;
reg.extend(extra_extensions);
Ok(reg)
}
}