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}