Repository: typst/typst
File: crates/typst-cli/src/args.rs (Rust)

Code before:
    /// Add an input value visible through sys.inputs
    /// whose type is always treated as a string
    /// which you may enclose with single or double quotes
    #[clap(
        long = "input",
        short = 'i',
        value_name = "KEY=VALUE",
        action = ArgAction::Append,
        value_parser = ValueParser::new(parse_input_pair),
    )]
    pub plain_inputs: Vec<(String, String)>,
}

fn parse_input_pair(raw: &str) -> Result<(String, String), InputPairError> {
    let (key, val) = raw.split_once('=').ok_or(InputPairError::NoEqual)?;
    let key = key.trim().to_owned();
    if key.is_empty() {
        return Err(InputPairError::MissingKey);
    }
    let val = val.trim().to_owned();
    Ok((key, val))
}

/// An error that occurred while parsing an input argument
#[derive(Clone, Debug)]
enum InputPairError {
    /// Missing equal sign in the value
    NoEqual,
    // The key was missing or entirely blank
    MissingKey,
}

impl Display for InputPairError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::NoEqual => write!(
                f,
                "input arguments must be a key and a value separated by an equal sign"
            ),
            Self::MissingKey => write!(f, "the key was missing or empty"),
        }
    }
}

impl std::error::Error for InputPairError {}

/// Lists all discovered fonts in system and custom font paths
#[derive(Debug, Clone, Parser)]
pub struct FontsCommand {
    /// Adds additional directories to search for fonts
    #[clap(

Review comment (suggestion):
If that would work with clap, I think we could directly return the strings in the error function above and skip this type.

Code after:

    /// The format to emit diagnostics in
    #[clap(
        long,
        default_value_t = DiagnosticFormat::Human,
        value_parser = clap::value_parser!(DiagnosticFormat)
    )]
    pub diagnostic_format: DiagnosticFormat,
}

/// Parses key/value pairs split by the first equal sign.
///
/// This function will return an error if the argument contains no equals sign
/// or contains the key (before the equals sign) is empty.
fn parse_input_pair(raw: &str) -> Result<(String, String), String> {
    let (key, val) = raw
        .split_once('=')
        .ok_or("input must be a key and a value separated by an equal sign")?;
    let key = key.trim().to_owned();
    if key.is_empty() {
        return Err("the key was missing or empty".to_owned());
    }
    let val = val.trim().to_owned();
    Ok((key, val))
}

/// Lists all discovered fonts in system and custom font paths
#[derive(Debug, Clone, Parser)]
pub struct FontsCommand {
    /// Adds additional directories to search for fonts
    #[clap(
        long = "font-path",
        env = "TYPST_FONT_PATHS",
        value_name = "DIR",
        value_delimiter = ENV_PATH_SEP,
    )]
    pub font_paths: Vec<PathBuf>,

    /// Also lists style variants of each font family
    #[arg(long)]
    pub variants: bool,
}

/// Which format to use for diagnostics.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ValueEnum)]
pub enum DiagnosticFormat {
    Human,
    Short,
}

impl Display for DiagnosticFormat {