storkit: merge 369_bug_cli_treats_help_and_version_as_project_paths

This commit is contained in:
dave
2026-03-23 12:51:59 +00:00
parent 56d3373e69
commit 1816a94617

View File

@@ -34,6 +34,32 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::broadcast; use tokio::sync::broadcast;
/// What the first CLI argument means.
#[derive(Debug, PartialEq)]
enum CliDirective {
/// `--help` / `-h`
Help,
/// `--version` / `-V`
Version,
/// An unrecognised flag (starts with `-`).
UnknownFlag(String),
/// A positional path argument.
Path,
/// No arguments at all.
None,
}
/// Inspect the raw CLI arguments and return the directive they imply.
fn classify_cli_args(args: &[String]) -> CliDirective {
match args.first().map(String::as_str) {
None => CliDirective::None,
Some("--help" | "-h") => CliDirective::Help,
Some("--version" | "-V") => CliDirective::Version,
Some(a) if a.starts_with('-') => CliDirective::UnknownFlag(a.to_string()),
Some(_) => CliDirective::Path,
}
}
/// Resolve the optional positional path argument (everything after the binary /// Resolve the optional positional path argument (everything after the binary
/// name) into an absolute `PathBuf`. Returns `None` when no argument was /// name) into an absolute `PathBuf`. Returns `None` when no argument was
/// supplied so that the caller can fall back to the auto-detect behaviour. /// supplied so that the caller can fall back to the auto-detect behaviour.
@@ -53,8 +79,61 @@ async fn main() -> Result<(), std::io::Error> {
// Collect CLI args, skipping the binary name (argv[0]). // Collect CLI args, skipping the binary name (argv[0]).
let cli_args: Vec<String> = std::env::args().skip(1).collect(); let cli_args: Vec<String> = std::env::args().skip(1).collect();
// Handle CLI flags before treating anything as a project path.
match classify_cli_args(&cli_args) {
CliDirective::Help => {
println!("storkit [PATH]");
println!();
println!("Serve a storkit project.");
println!();
println!("USAGE:");
println!(" storkit [PATH]");
println!();
println!("ARGS:");
println!(
" PATH Path to an existing project directory. \
If omitted, storkit searches parent directories for a .storkit/ root."
);
println!();
println!("OPTIONS:");
println!(" -h, --help Print this help and exit");
println!(" -V, --version Print the version and exit");
std::process::exit(0);
}
CliDirective::Version => {
println!("storkit {}", env!("CARGO_PKG_VERSION"));
std::process::exit(0);
}
CliDirective::UnknownFlag(flag) => {
eprintln!("error: unknown option: {flag}");
eprintln!("Run 'storkit --help' for usage.");
std::process::exit(1);
}
CliDirective::Path | CliDirective::None => {}
}
let explicit_path = parse_project_path_arg(&cli_args, &cwd); let explicit_path = parse_project_path_arg(&cli_args, &cwd);
// When a path is given explicitly on the CLI, it must already exist as a
// directory. We do not create directories from the command line.
if let Some(ref path) = explicit_path {
if !path.exists() {
eprintln!(
"error: path does not exist: {}",
path.display()
);
std::process::exit(1);
}
if !path.is_dir() {
eprintln!(
"error: path is not a directory: {}",
path.display()
);
std::process::exit(1);
}
}
if let Some(explicit_root) = explicit_path { if let Some(explicit_root) = explicit_path {
// An explicit path was given on the command line. // An explicit path was given on the command line.
// Open it directly — scaffold .storkit/ if it is missing — and // Open it directly — scaffold .storkit/ if it is missing — and
@@ -399,6 +478,61 @@ name = "coder"
.unwrap_or_else(|e| panic!("Invalid project.toml: {e}")); .unwrap_or_else(|e| panic!("Invalid project.toml: {e}"));
} }
// ── classify_cli_args ─────────────────────────────────────────────────
#[test]
fn classify_none_when_no_args() {
assert_eq!(classify_cli_args(&[]), CliDirective::None);
}
#[test]
fn classify_help_long() {
assert_eq!(
classify_cli_args(&["--help".to_string()]),
CliDirective::Help
);
}
#[test]
fn classify_help_short() {
assert_eq!(
classify_cli_args(&["-h".to_string()]),
CliDirective::Help
);
}
#[test]
fn classify_version_long() {
assert_eq!(
classify_cli_args(&["--version".to_string()]),
CliDirective::Version
);
}
#[test]
fn classify_version_short() {
assert_eq!(
classify_cli_args(&["-V".to_string()]),
CliDirective::Version
);
}
#[test]
fn classify_unknown_flag() {
assert_eq!(
classify_cli_args(&["--serve".to_string()]),
CliDirective::UnknownFlag("--serve".to_string())
);
}
#[test]
fn classify_path() {
assert_eq!(
classify_cli_args(&["/some/path".to_string()]),
CliDirective::Path
);
}
// ── parse_project_path_arg ──────────────────────────────────────────── // ── parse_project_path_arg ────────────────────────────────────────────
#[test] #[test]