use crate::ident::validate_rust_path;
use clap::{Parser, error::ErrorKind};
use std::path::PathBuf;
#[derive(Parser, Clone, Debug, Eq, PartialEq)]
#[command(name = "ph-haptics-gen")]
#[command(version)]
#[command(about = "Compile .phh haptics definitions into Rust constants")]
pub(crate) struct Cli {
#[arg(long, value_name = "PATH")]
pub(crate) input: PathBuf,
#[arg(long, value_name = "PATH")]
pub(crate) output: PathBuf,
#[arg(long, value_name = "PATH")]
pub(crate) profiles_toml: Option<PathBuf>,
#[arg(long, default_value = "crate::curves")]
pub(crate) curves_module: String,
#[arg(long, default_value = "ph_haptics")]
pub(crate) haptics_crate: String,
#[arg(long, value_name = "PATH")]
pub(crate) curves_file: Option<PathBuf>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum CliAction {
Run(Cli),
Help(String),
}
pub(crate) fn parse_cli(args: impl Iterator<Item = String>) -> Result<CliAction, String> {
let mut argv = vec![String::from("ph-haptics-gen")];
argv.extend(args);
match Cli::try_parse_from(argv) {
Ok(cli) => {
validate_cli_paths(&cli)?;
Ok(CliAction::Run(cli))
}
Err(error) => match error.kind() {
ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
Ok(CliAction::Help(error.to_string()))
}
_ => Err(error.to_string()),
},
}
}
fn validate_cli_paths(cli: &Cli) -> Result<(), String> {
validate_rust_path(&cli.curves_module, "--curves-module")?;
validate_rust_path(&cli.haptics_crate, "--haptics-crate")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_help_flag_returns_help_action() {
let action = parse_cli(["--help".to_owned()].into_iter()).unwrap();
match action {
CliAction::Help(help) => assert!(help.contains("Usage:")),
CliAction::Run(_) => panic!("expected help action"),
}
}
#[test]
fn parse_minimal_required_args() {
let action = parse_cli(
[
"--input".to_owned(),
"haptics.phh".to_owned(),
"--output".to_owned(),
"haptics_compiled.rs".to_owned(),
]
.into_iter(),
)
.unwrap();
let CliAction::Run(cli) = action else {
panic!("expected run action");
};
assert_eq!(cli.input, PathBuf::from("haptics.phh"));
assert_eq!(cli.output, PathBuf::from("haptics_compiled.rs"));
assert_eq!(cli.curves_module, "crate::curves");
assert_eq!(cli.haptics_crate, "ph_haptics");
}
#[test]
fn rejects_invalid_curves_module_path() {
let err = parse_cli(
[
"--input".to_owned(),
"haptics.phh".to_owned(),
"--output".to_owned(),
"out.rs".to_owned(),
"--curves-module".to_owned(),
"crate/curves".to_owned(),
]
.into_iter(),
)
.unwrap_err();
assert!(err.contains("--curves-module"), "error: {err}");
}
#[test]
fn rejects_invalid_haptics_crate_path() {
let err = parse_cli(
[
"--input".to_owned(),
"haptics.phh".to_owned(),
"--output".to_owned(),
"out.rs".to_owned(),
"--haptics-crate".to_owned(),
"ph-haptics".to_owned(),
]
.into_iter(),
)
.unwrap_err();
assert!(err.contains("--haptics-crate"), "error: {err}");
}
}