hugr_cli/lib.rs
1//! Standard command line tools for the HUGR format.
2//!
3//! This library provides utilities for the HUGR CLI.
4//!
5//! ## CLI Usage
6//!
7//! Run `cargo install hugr-cli` to install the CLI tools. This will make the
8//! `hugr` executable available in your shell as long as you have [cargo's bin
9//! directory](https://doc.rust-lang.org/book/ch14-04-installing-binaries.html)
10//! in your path.
11//!
12//! The CLI provides two subcommands:
13//!
14//! - `validate` for validating HUGR files.
15//! - `mermaid` for visualizing HUGR files as mermaid diagrams.
16//!
17//! ### Validate
18//!
19//! Validate and visualize a HUGR file
20//!
21//! Usage: `hugr validate [OPTIONS] [INPUT]`
22//!
23//! ```text
24//! Options:
25//! -v, --verbose... Increase logging verbosity
26//! -q, --quiet... Decrease logging verbosity
27//! -h, --help Print help (see more with '--help')
28//! -V, --version Print version
29//!
30//! Input:
31//! --no-std Don't use standard extensions when validating hugrs. Prelude is still used.
32//! -e, --extensions <EXTENSIONS> Paths to serialised extensions to validate against.
33//! --hugr-json Read the input as a HUGR JSON file instead of an envelope
34//! [INPUT] Input file. Defaults to `-` for stdin
35//! ```
36//!
37//! ### Mermaid
38//!
39//! Write HUGR as mermaid diagrams
40//!
41//! Usage: `hugr mermaid [OPTIONS] [INPUT]`
42//!
43//! ```text
44//! Options:
45//! --validate Validate before rendering, includes extension inference.
46//! -o, --output <OUTPUT> Output file '-' for stdout [default: -]
47//! -v, --verbose... Increase logging verbosity
48//! -q, --quiet... Decrease logging verbosity
49//! -h, --help Print help (see more with '--help')
50//! -V, --version Print version
51//!
52//! Input:
53//! --no-std Don't use standard extensions when validating hugrs. Prelude is still used.
54//! -e, --extensions <EXTENSIONS> Paths to serialised extensions to validate against.
55//! --hugr-json Read the input as a HUGR JSON file instead of an envelope
56//! [INPUT] Input file. Defaults to `-` for stdin.
57//! ```
58
59use clap::{Parser, crate_version};
60use clap_verbosity_flag::{InfoLevel, Verbosity};
61use hugr::envelope::EnvelopeError;
62use hugr::package::PackageValidationError;
63use std::ffi::OsString;
64use thiserror::Error;
65
66pub mod convert;
67pub mod extensions;
68pub mod hugr_io;
69pub mod mermaid;
70pub mod validate;
71
72/// CLI arguments.
73#[derive(Parser, Debug)]
74#[clap(version = crate_version!(), long_about = None)]
75#[clap(about = "HUGR CLI tools.")]
76#[group(id = "hugr")]
77pub struct CliArgs {
78 /// The command to be run.
79 #[command(subcommand)]
80 pub command: CliCommand,
81 /// Verbosity.
82 #[command(flatten)]
83 pub verbose: Verbosity<InfoLevel>,
84}
85
86/// The CLI subcommands.
87#[derive(Debug, clap::Subcommand)]
88#[non_exhaustive]
89pub enum CliCommand {
90 /// Validate and visualize a HUGR file.
91 Validate(validate::ValArgs),
92 /// Write standard extensions out in serialized form.
93 GenExtensions(extensions::ExtArgs),
94 /// Write HUGR as mermaid diagrams.
95 Mermaid(mermaid::MermaidArgs),
96 /// Convert between different HUGR envelope formats.
97 Convert(convert::ConvertArgs),
98 /// External commands
99 #[command(external_subcommand)]
100 External(Vec<OsString>),
101}
102
103/// Error type for the CLI.
104#[derive(Debug, Error)]
105#[non_exhaustive]
106pub enum CliError {
107 /// Error reading input.
108 #[error("Error reading from path.")]
109 InputFile(#[from] std::io::Error),
110 /// Error parsing input.
111 #[error("Error parsing package.")]
112 Parse(#[from] serde_json::Error),
113 #[error("Error validating HUGR.")]
114 /// Errors produced by the `validate` subcommand.
115 Validate(#[from] PackageValidationError),
116 #[error("Error decoding HUGR envelope.")]
117 /// Errors produced by the `validate` subcommand.
118 Envelope(#[from] EnvelopeError),
119 /// Pretty error when the user passes a non-envelope file.
120 #[error(
121 "Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead."
122 )]
123 NotAnEnvelope,
124 /// Invalid format string for conversion.
125 #[error(
126 "Invalid format: '{_0}'. Valid formats are: json, model, model-exts, model-text, model-text-exts"
127 )]
128 InvalidFormat(String),
129 #[error("Error validating HUGR generated by {generator}")]
130 /// Errors produced by the `validate` subcommand, with a known generator of the HUGR.
131 ValidateKnownGenerator {
132 #[source]
133 /// The inner validation error.
134 inner: PackageValidationError,
135 /// The generator of the HUGR.
136 generator: Box<String>,
137 },
138}
139
140impl CliError {
141 /// Returns a validation error, with an optional generator.
142 pub fn validation(generator: Option<String>, val_err: PackageValidationError) -> Self {
143 if let Some(g) = generator {
144 Self::ValidateKnownGenerator {
145 inner: val_err,
146 generator: Box::new(g.to_string()),
147 }
148 } else {
149 Self::Validate(val_err)
150 }
151 }
152}