runner/
cli.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![deny(clippy::all, clippy::pedantic)]
5
6use clap::error::ErrorKind;
7use clap::{Command, arg, crate_version, value_parser};
8use std::io::IsTerminal;
9use std::{ffi::OsString, path::PathBuf};
10
11/// # Errors
12/// Returns an error if the arguments are invalid.
13/// # Panics
14/// Panics if the arguments cannot be read.
15pub fn main<I, T>(args: Option<I>) -> Result<(), String>
16where
17    I: IntoIterator<Item = T>,
18    T: Into<OsString> + Clone,
19{
20    let cmd = Command::new("qir-runner").args(&[
21        arg!(-f --file <PATH> "Path to the QIR file to run. If not provided or '-', standard input will be read.")
22            .value_parser(value_parser!(PathBuf)),
23        arg!(-e --entrypoint <NAME> "Name of the entry point function to execute"),
24        arg!(-s --shots <NUM> "The number of times to repeat the execution of the chosen entry point in the program")
25            .value_parser(value_parser!(u32))
26            .default_value("1"),
27        arg!(-r --rngseed <NUM> "The value to use when seeding the random number generator used for quantum simulation")
28            .value_parser(value_parser!(u64))
29        ]).version(crate_version!());
30    let mut help_cmd = cmd.clone();
31    let matches = match args {
32        Some(args) => cmd.try_get_matches_from(args),
33        None => cmd.try_get_matches(),
34    };
35    match matches {
36        Err(e) => {
37            let msg = e.to_string();
38            match e.kind() {
39                ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
40                    eprint!("{msg}");
41                    Ok(())
42                }
43                _ => Err(msg),
44            }
45        }
46        Ok(matches) => {
47            let file = matches.get_one::<PathBuf>("file").and_then(|path| {
48                if path.as_os_str() == "-" {
49                    None
50                } else {
51                    Some(path)
52                }
53            });
54            let entry_point = matches
55                .get_one::<String>("entrypoint")
56                .map(std::string::String::as_str);
57            let shots = *matches
58                .get_one::<u32>("shots")
59                .expect("Shots is required or should have a default value");
60            let rng_seed = matches
61                .try_get_one::<u64>("rngseed")
62                .map_or(None, Option::<&u64>::copied);
63            let output = &mut std::io::stdout();
64
65            if let Some(path) = file {
66                crate::run_file(path, entry_point, shots, rng_seed, output)
67            } else {
68                if std::io::stdin().is_terminal() {
69                    return help_cmd.print_help().map_err(|e| e.to_string());
70                }
71                crate::run_input(
72                    &mut std::io::stdin().lock(),
73                    entry_point,
74                    shots,
75                    rng_seed,
76                    output,
77                )
78            }
79        }
80    }
81}