1use clap::{crate_version, Parser};
4use clap_verbosity_flag::{InfoLevel, Verbosity};
5use clio::Input;
6use derive_more::{Display, Error, From};
7use hugr::extension::ExtensionRegistry;
8use hugr::package::{PackageEncodingError, PackageValidationError};
9use hugr::Hugr;
10use std::io::{Cursor, Read, Seek, SeekFrom};
11use std::{ffi::OsString, path::PathBuf};
12
13pub mod extensions;
14pub mod mermaid;
15pub mod validate;
16
17use hugr::package::Package;
18
19#[derive(Parser, Debug)]
21#[clap(version = crate_version!(), long_about = None)]
22#[clap(about = "HUGR CLI tools.")]
23#[group(id = "hugr")]
24#[non_exhaustive]
25pub enum CliArgs {
26 Validate(validate::ValArgs),
28 GenExtensions(extensions::ExtArgs),
30 Mermaid(mermaid::MermaidArgs),
32 #[command(external_subcommand)]
34 External(Vec<OsString>),
35}
36
37#[derive(Debug, Display, Error, From)]
39#[non_exhaustive]
40pub enum CliError {
41 #[display("Error reading from path: {_0}")]
43 InputFile(std::io::Error),
44 #[display("Error parsing package: {_0}")]
46 Parse(serde_json::Error),
47 #[display("Error parsing package: {_0}")]
49 HUGRLoad(PackageEncodingError),
50 #[display("Error validating HUGR: {_0}")]
51 Validate(PackageValidationError),
53}
54
55#[derive(Parser, Debug)]
57pub struct HugrArgs {
58 #[clap(value_parser, default_value = "-")]
60 pub input: Input,
61 #[command(flatten)]
63 pub verbose: Verbosity<InfoLevel>,
64 #[arg(
66 long,
67 help = "Don't use standard extensions when validating. Prelude is still used."
68 )]
69 pub no_std: bool,
70 #[arg(
72 short,
73 long,
74 help = "Paths to serialised extensions to validate against."
75 )]
76 pub extensions: Vec<PathBuf>,
77}
78
79#[derive(Debug, Clone, PartialEq)]
83pub enum PackageOrHugr {
84 Package(Package),
86 Hugr(Hugr),
88}
89
90impl PackageOrHugr {
91 pub fn into_hugrs(self) -> Vec<Hugr> {
93 match self {
94 PackageOrHugr::Package(pkg) => pkg.modules,
95 PackageOrHugr::Hugr(hugr) => vec![hugr],
96 }
97 }
98
99 pub fn validate(&self) -> Result<(), PackageValidationError> {
101 match self {
102 PackageOrHugr::Package(pkg) => pkg.validate(),
103 PackageOrHugr::Hugr(hugr) => Ok(hugr.validate()?),
104 }
105 }
106}
107
108impl AsRef<[Hugr]> for PackageOrHugr {
109 fn as_ref(&self) -> &[Hugr] {
110 match self {
111 PackageOrHugr::Package(pkg) => &pkg.modules,
112 PackageOrHugr::Hugr(hugr) => std::slice::from_ref(hugr),
113 }
114 }
115}
116
117impl HugrArgs {
118 pub fn get_package_or_hugr(
120 &mut self,
121 extensions: &ExtensionRegistry,
122 ) -> Result<PackageOrHugr, CliError> {
123 match self.input.can_seek() {
127 true => get_package_or_hugr_seek(&mut self.input, extensions),
128 false => {
129 let mut buffer = Vec::new();
130 self.input.read_to_end(&mut buffer)?;
131 get_package_or_hugr_seek(Cursor::new(buffer), extensions)
132 }
133 }
134 }
135}
136
137fn get_package_or_hugr_seek<I: Seek + Read>(
139 mut input: I,
140 extensions: &ExtensionRegistry,
141) -> Result<PackageOrHugr, CliError> {
142 match Hugr::load_json(&mut input, extensions) {
143 Ok(hugr) => Ok(PackageOrHugr::Hugr(hugr)),
144 Err(_) => {
145 input.seek(SeekFrom::Start(0))?;
146 let pkg = Package::from_json_reader(input, extensions)?;
147 Ok(PackageOrHugr::Package(pkg))
148 }
149 }
150}