use std::path::PathBuf;
use clap::{Args, Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(name = "frame", version, about = "Frame application tooling")]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
}
#[derive(Debug, Subcommand)]
pub enum Command {
New {
name: String,
},
Run,
Dev {
#[arg(long, default_value_t = 100)]
quiet_ms: u64,
},
Test(TestScope),
Check,
Doctor,
Build,
Host {
#[arg(long)]
config: PathBuf,
},
}
#[derive(Args, Clone, Copy, Debug, Default)]
pub struct TestScope {
#[arg(long)]
pub component: bool,
#[arg(long)]
pub host: bool,
#[arg(long)]
pub browser: bool,
}
impl TestScope {
#[must_use]
pub fn is_everything(self) -> bool {
!(self.component || self.host || self.browser)
}
#[must_use]
pub fn component_selected(self) -> bool {
self.component || self.is_everything()
}
#[must_use]
pub fn host_selected(self) -> bool {
self.host || self.is_everything()
}
#[must_use]
pub fn browser_selected(self) -> bool {
self.browser || self.is_everything()
}
}
#[cfg(test)]
mod tests {
use super::{Cli, Command, TestScope};
use clap::Parser;
#[test]
fn clap_takes_bare_name_and_rejects_removed_and_unknown_flags() {
assert!(Cli::try_parse_from(["frame", "new", "valid_app"]).is_ok());
assert!(Cli::try_parse_from(["frame", "new", "valid_app", "--frame-root", "."]).is_err());
assert!(Cli::try_parse_from(["frame", "new", "valid_app", "--force"]).is_err());
}
#[test]
fn test_scope_defaults_to_everything_and_flags_narrow_to_a_union() -> Result<(), String> {
let default = TestScope::default();
assert!(default.is_everything());
assert!(default.component_selected() && default.host_selected());
assert!(default.browser_selected());
let parsed = Cli::try_parse_from(["frame", "test", "--component", "--browser"])
.map_err(|error| error.to_string())?;
let Command::Test(scope) = parsed.command else {
return Err("frame test --component --browser parsed to the wrong verb".to_owned());
};
assert!(!scope.is_everything());
assert!(scope.component_selected());
assert!(scope.browser_selected());
assert!(!scope.host_selected());
Ok(())
}
#[test]
fn every_verb_parses_and_host_requires_its_config() {
for verb in ["run", "check", "doctor", "build"] {
assert!(Cli::try_parse_from(["frame", verb]).is_ok(), "{verb}");
}
assert!(Cli::try_parse_from(["frame", "host", "--config", "frame.toml"]).is_ok());
assert!(
Cli::try_parse_from(["frame", "host"]).is_err(),
"frame host without --config must be a parse error"
);
assert!(
Cli::try_parse_from(["frame", "e2e"]).is_err(),
"e2e is dead as a command name"
);
}
}