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 top level help can be accessed with:
13//! ```sh
14//! hugr --help
15//! ```
16//!
17//! Refer to the help for each subcommand for more information, e.g.
18//! ```sh
19//! hugr validate --help
20//! ```
21
22use clap::{Parser, crate_version};
23use clap_verbosity_flag::{InfoLevel, Verbosity};
24use hugr::envelope::EnvelopeError;
25use hugr::package::PackageValidationError;
26use std::ffi::OsString;
27use thiserror::Error;
28
29pub mod convert;
30pub mod extensions;
31pub mod hugr_io;
32pub mod mermaid;
33pub mod validate;
34
35/// CLI arguments.
36#[derive(Parser, Debug)]
37#[clap(version = crate_version!(), long_about = None)]
38#[clap(about = "HUGR CLI tools.")]
39#[group(id = "hugr")]
40pub struct CliArgs {
41    /// The command to be run.
42    #[command(subcommand)]
43    pub command: CliCommand,
44    /// Verbosity.
45    #[command(flatten)]
46    pub verbose: Verbosity<InfoLevel>,
47}
48
49/// The CLI subcommands.
50#[derive(Debug, clap::Subcommand)]
51#[non_exhaustive]
52pub enum CliCommand {
53    /// Validate a HUGR package.
54    Validate(validate::ValArgs),
55    /// Write standard extensions out in serialized form.
56    GenExtensions(extensions::ExtArgs),
57    /// Write HUGR as mermaid diagrams.
58    Mermaid(mermaid::MermaidArgs),
59    /// Convert between different HUGR envelope formats.
60    Convert(convert::ConvertArgs),
61    /// External commands
62    #[command(external_subcommand)]
63    External(Vec<OsString>),
64}
65
66/// Error type for the CLI.
67#[derive(Debug, Error)]
68#[non_exhaustive]
69pub enum CliError {
70    /// Error reading input.
71    #[error("Error reading from path.")]
72    InputFile(#[from] std::io::Error),
73    /// Error parsing input.
74    #[error("Error parsing package.")]
75    Parse(#[from] serde_json::Error),
76    #[error("Error validating HUGR.")]
77    /// Errors produced by the `validate` subcommand.
78    Validate(#[from] PackageValidationError),
79    #[error("Error decoding HUGR envelope.")]
80    /// Errors produced by the `validate` subcommand.
81    Envelope(#[from] EnvelopeError),
82    /// Pretty error when the user passes a non-envelope file.
83    #[error(
84        "Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead."
85    )]
86    NotAnEnvelope,
87    /// Invalid format string for conversion.
88    #[error(
89        "Invalid format: '{_0}'. Valid formats are: json, model, model-exts, model-text, model-text-exts"
90    )]
91    InvalidFormat(String),
92    #[error("Error validating HUGR generated by {generator}")]
93    /// Errors produced by the `validate` subcommand, with a known generator of the HUGR.
94    ValidateKnownGenerator {
95        #[source]
96        /// The inner validation error.
97        inner: PackageValidationError,
98        /// The generator of the HUGR.
99        generator: Box<String>,
100    },
101}
102
103impl CliError {
104    /// Returns a validation error, with an optional generator.
105    pub fn validation(generator: Option<String>, val_err: PackageValidationError) -> Self {
106        if let Some(g) = generator {
107            Self::ValidateKnownGenerator {
108                inner: val_err,
109                generator: Box::new(g.to_string()),
110            }
111        } else {
112            Self::Validate(val_err)
113        }
114    }
115}