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 describe;
31pub mod extensions;
32pub mod hugr_io;
33pub mod mermaid;
34pub mod validate;
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    /// Describe the contents of a HUGR package.
66    ///
67    /// If an error occurs during loading partial descriptions are printed.
68    /// For example if the first module is loaded and the second fails then
69    /// only the first module will be described.
70    Describe(describe::DescribeArgs),
71}
72
73/// Error type for the CLI.
74#[derive(Debug, Error)]
75#[non_exhaustive]
76pub enum CliError {
77    /// Error reading input.
78    #[error("Error reading from path.")]
79    InputFile(#[from] std::io::Error),
80    /// Error parsing input.
81    #[error("Error parsing package.")]
82    Parse(#[from] serde_json::Error),
83    #[error("Error validating HUGR.")]
84    /// Errors produced by the `validate` subcommand.
85    Validate(#[from] PackageValidationError),
86    #[error("Error decoding HUGR envelope.")]
87    /// Errors produced by the `validate` subcommand.
88    Envelope(#[from] EnvelopeError),
89    /// Pretty error when the user passes a non-envelope file.
90    #[error(
91        "Input file is not a HUGR envelope. Invalid magic number.\n\nUse `--hugr-json` to read a raw HUGR JSON file instead."
92    )]
93    NotAnEnvelope,
94    /// Invalid format string for conversion.
95    #[error(
96        "Invalid format: '{_0}'. Valid formats are: json, model, model-exts, model-text, model-text-exts"
97    )]
98    InvalidFormat(String),
99    #[error("Error validating HUGR generated by {generator}")]
100    /// Errors produced by the `validate` subcommand, with a known generator of the HUGR.
101    ValidateKnownGenerator {
102        #[source]
103        /// The inner validation error.
104        inner: PackageValidationError,
105        /// The generator of the HUGR.
106        generator: Box<String>,
107    },
108    #[error("Error reading envelope.")]
109    /// Errors produced when reading an envelope.
110    ReadEnvelope(#[from] hugr::envelope::ReadError),
111}
112
113impl CliError {
114    /// Returns a validation error, with an optional generator.
115    pub fn validation(generator: Option<String>, val_err: PackageValidationError) -> Self {
116        if let Some(g) = generator {
117            Self::ValidateKnownGenerator {
118                inner: val_err,
119                generator: Box::new(g.to_string()),
120            }
121        } else {
122            Self::Validate(val_err)
123        }
124    }
125}