1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Standard command line tools, used by the hugr binary.

use clap_stdin::FileOrStdin;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use thiserror::Error;
/// We reexport some clap types that are used in the public API.
pub use {clap::Parser, clap_verbosity_flag::Level};

use hugr_core::{extension::ExtensionRegistry, Hugr, HugrView};

/// Validate and visualise a HUGR file.
#[derive(Parser, Debug)]
#[clap(version = "1.0", long_about = None)]
#[clap(about = "Validate a HUGR.")]
#[group(id = "hugr")]
pub struct CmdLineArgs {
    input: FileOrStdin,
    /// Visualise with mermaid.
    #[arg(short, long, value_name = "MERMAID", help = "Visualise with mermaid.")]
    mermaid: bool,
    /// Skip validation.
    #[arg(short, long, help = "Skip validation.")]
    no_validate: bool,
    /// Verbosity.
    #[command(flatten)]
    verbose: Verbosity<InfoLevel>,
    // TODO YAML extensions
}

/// Error type for the CLI.
#[derive(Error, Debug)]
pub enum CliError {
    /// Error reading input.
    #[error("Error reading input: {0}")]
    Input(#[from] clap_stdin::StdinError),
    /// Error parsing input.
    #[error("Error parsing input: {0}")]
    Parse(#[from] serde_json::Error),
    /// Error validating HUGR.
    #[error("Error validating HUGR: {0}")]
    Validate(#[from] hugr_core::hugr::ValidationError),
}

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

impl CmdLineArgs {
    /// Run the HUGR cli and validate against an extension registry.
    pub fn run(&self, registry: &ExtensionRegistry) -> Result<Hugr, CliError> {
        let mut hugr: Hugr = serde_json::from_reader(self.input.clone().into_reader()?)?;
        if self.mermaid {
            println!("{}", hugr.mermaid_string());
        }

        if !self.no_validate {
            hugr.update_validate(registry)?;
            if self.verbosity(Level::Info) {
                eprintln!("{}", VALID_PRINT);
            }
        }
        Ok(hugr)
    }

    /// Test whether a `level` message should be output.
    pub fn verbosity(&self, level: Level) -> bool {
        self.verbose.log_level_filter() >= level
    }
}