Skip to content

Commit

Permalink
feat(hugr-cli)!: move mermaid to own sub-command
Browse files Browse the repository at this point in the history
  • Loading branch information
ss2165 committed Aug 2, 2024
1 parent 7989adc commit 801e265
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 120 deletions.
46 changes: 45 additions & 1 deletion hugr-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use clio::Input;
use hugr_core::{Extension, Hugr};
use std::{ffi::OsString, path::PathBuf};
use thiserror::Error;

pub mod extensions;
pub mod mermaid;
pub mod validate;

/// CLI arguments.
Expand All @@ -20,6 +22,8 @@ pub enum CliArgs {
Validate(validate::ValArgs),
/// Write standard extensions out in serialized form.
GenExtensions(extensions::ExtArgs),
/// Write HUGR as mermaid diagrams.
Mermaid(mermaid::MermaidArgs),
/// External commands
#[command(external_subcommand)]
External(Vec<OsString>),
Expand All @@ -30,8 +34,14 @@ pub enum CliArgs {
#[error(transparent)]
#[non_exhaustive]
pub enum CliError {
/// Error reading input.
#[error("Error reading from path: {0}")]
InputFile(#[from] std::io::Error),
/// Error parsing input.
#[error("Error parsing input: {0}")]
Parse(#[from] serde_json::Error),
/// Errors produced by the `validate` subcommand.
Validate(#[from] validate::CliError),
Validate(#[from] validate::ValError),
}

/// Validate and visualise a HUGR file.
Expand All @@ -50,3 +60,37 @@ pub struct HugrArgs {
#[arg(short, long, help = "Skip validation.")]
pub extensions: Vec<PathBuf>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
/// Package of module HUGRs and extensions.
/// The HUGRs are validated against the extensions.
pub struct Package {
/// Module HUGRs included in the package.
pub modules: Vec<Hugr>,
/// Extensions to validate against.
pub extensions: Vec<Extension>,
}

impl Package {
/// Create a new package.
pub fn new(modules: Vec<Hugr>, extensions: Vec<Extension>) -> Self {
Self {
modules,
extensions,
}
}
}

impl HugrArgs {
/// Read either a package or a single hugr from the input.
pub fn get_package(&mut self) -> Result<Package, CliError> {
let val: serde_json::Value = serde_json::from_reader(&mut self.input)?;
// read either a package or a single hugr
if let Ok(p) = serde_json::from_value::<Package>(val.clone()) {
Ok(p)
} else {
let hugr: Hugr = serde_json::from_value(val)?;
Ok(Package::new(vec![hugr], vec![]))
}
}
}
1 change: 1 addition & 0 deletions hugr-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ fn main() {
match CliArgs::parse() {
CliArgs::Validate(args) => run_validate(args),
CliArgs::GenExtensions(args) => args.run_dump(),
CliArgs::Mermaid(mut args) => args.run_print().unwrap(),
CliArgs::External(_) => {
// TODO: Implement support for external commands.
// Running `hugr COMMAND` would look for `hugr-COMMAND` in the path
Expand Down
32 changes: 32 additions & 0 deletions hugr-cli/src/mermaid.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//! Render mermaid diagrams.
use std::io::Write;

use clap::Parser;
use clio::Output;
use hugr_core::HugrView;

/// Dump the standard extensions.
#[derive(Parser, Debug)]
#[clap(version = "1.0", long_about = None)]
#[clap(about = "Render mermaid diagrams..")]
#[group(id = "hugr")]
#[non_exhaustive]
pub struct MermaidArgs {
/// Common arguments
#[command(flatten)]
pub hugr_args: crate::HugrArgs,
/// Output file '-' for stdout
#[clap(long, short, value_parser, default_value = "-")]
output: Output,
}

impl MermaidArgs {
/// Write the mermaid diagram to the output.
pub fn run_print(&mut self) -> Result<(), crate::CliError> {
let package = self.hugr_args.get_package()?;
for hugr in package.modules {
write!(self.output, "{}", hugr.mermaid_string())?;
}
Ok(())
}
}
79 changes: 12 additions & 67 deletions hugr-cli/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

use clap::Parser;
use clap_verbosity_flag::Level;
use hugr_core::{extension::ExtensionRegistry, Extension, Hugr, HugrView as _};
use hugr_core::{extension::ExtensionRegistry, Extension, Hugr};
use thiserror::Error;

use crate::HugrArgs;
use crate::{CliError, HugrArgs, Package};

/// Validate and visualise a HUGR file.
#[derive(Parser, Debug)]
Expand All @@ -17,24 +17,12 @@ pub struct ValArgs {
#[command(flatten)]
/// common arguments
pub hugr_args: HugrArgs,
/// Visualise with mermaid.
#[arg(short, long, value_name = "MERMAID", help = "Visualise with mermaid.")]
pub mermaid: bool,
/// Skip validation.
#[arg(short, long, help = "Skip validation.")]
pub no_validate: bool,
}

/// Error type for the CLI.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum CliError {
/// Error reading input.
#[error("Error reading from path: {0}")]
InputFile(#[from] std::io::Error),
/// Error parsing input.
#[error("Error parsing input: {0}")]
Parse(#[from] serde_json::Error),
pub enum ValError {
/// Error validating HUGR.
#[error("Error validating HUGR: {0}")]
Validate(#[from] hugr_core::hugr::ValidationError),
Expand All @@ -43,53 +31,16 @@ pub enum CliError {
ExtReg(#[from] hugr_core::extension::ExtensionRegistryError),
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
/// Package of module HUGRs and extensions.
/// The HUGRs are validated against the extensions.
pub struct Package {
modules: Vec<Hugr>,
extensions: Vec<Extension>,
}

impl Package {
/// Create a new package.
pub fn new(modules: Vec<Hugr>, extensions: Vec<Extension>) -> Self {
Self {
modules,
extensions,
}
}

/// Modules in the package.
pub fn modules(&self) -> &[Hugr] {
&self.modules
}

/// Extensions in the package.
pub fn extensions(&self) -> &[Extension] {
&self.extensions
}
}

/// String to print when validation is successful.
pub const VALID_PRINT: &str = "HUGR valid!";

impl ValArgs {
/// Run the HUGR cli and validate against an extension registry.
pub fn run(&mut self) -> Result<Vec<Hugr>, CliError> {
// let rdr = self.input.
let val: serde_json::Value = serde_json::from_reader(&mut self.hugr_args.input)?;
// read either a package or a single hugr
let (mut modules, packed_exts) = if let Ok(Package {
modules,
extensions,
}) = serde_json::from_value::<Package>(val.clone())
{
(modules, extensions)
} else {
let hugr: Hugr = serde_json::from_value(val)?;
(vec![hugr], vec![])
};
let Package {
mut modules,
extensions: packed_exts,
} = self.hugr_args.get_package()?;

let mut reg: ExtensionRegistry = if self.hugr_args.no_std {
hugr_core::extension::PRELUDE_REGISTRY.to_owned()
Expand All @@ -99,26 +50,20 @@ impl ValArgs {

// register packed extensions
for ext in packed_exts {
reg.register_updated(ext)?;
reg.register_updated(ext).map_err(ValError::ExtReg)?;
}

// register external extensions
for ext in &self.hugr_args.extensions {
let f = std::fs::File::open(ext)?;
let ext: Extension = serde_json::from_reader(f)?;
reg.register_updated(ext)?;
reg.register_updated(ext).map_err(ValError::ExtReg)?;
}

for hugr in modules.iter_mut() {
if self.mermaid {
println!("{}", hugr.mermaid_string());
}

if !self.no_validate {
hugr.update_validate(&reg)?;
if self.verbosity(Level::Info) {
eprintln!("{}", VALID_PRINT);
}
hugr.update_validate(&reg).map_err(ValError::Validate)?;
if self.verbosity(Level::Info) {
eprintln!("{}", VALID_PRINT);
}
}
Ok(modules)
Expand Down
Loading

0 comments on commit 801e265

Please sign in to comment.