aboutsummaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/plugin.rs123
1 files changed, 103 insertions, 20 deletions
diff --git a/src/cli/plugin.rs b/src/cli/plugin.rs
index a2c1e6c3..46caf509 100644
--- a/src/cli/plugin.rs
+++ b/src/cli/plugin.rs
@@ -15,6 +15,8 @@ use crate::api::schema::{
use crate::popup_size::PopupSize;
const PLUGIN_BUILD_OUTPUT_MAX_BYTES: usize = 64 * 1024;
+const PLUGIN_INSTALL_USAGE: &str =
+ "usage: herdr plugin install [--ref REF] [--yes|-y] <owner>/<repo>[/subdir...]";
pub(super) fn run_plugin_command(args: &[String]) -> std::io::Result<i32> {
let Some(subcommand) = args.first().map(|arg| arg.as_str()) else {
@@ -151,39 +153,58 @@ fn plugin_unlink(args: &[String]) -> std::io::Result<i32> {
}))
}
-fn plugin_install(args: &[String]) -> std::io::Result<i32> {
- let Some(source_arg) = args.first() else {
- eprintln!("usage: herdr plugin install <owner>/<repo>[/subdir...] [--ref REF] [--yes]");
- return Ok(2);
- };
- let source = match GithubPluginSource::parse(source_arg) {
- Ok(source) => source,
- Err(err) => {
- eprintln!("{err}");
- return Ok(2);
- }
- };
+#[derive(Debug)]
+struct PluginInstallArgs {
+ source: GithubPluginSource,
+ requested_ref: Option<String>,
+ yes: bool,
+}
+
+fn parse_plugin_install_args(args: &[String]) -> Result<PluginInstallArgs, String> {
+ let mut source_arg = None;
let mut requested_ref = None;
let mut yes = false;
- let mut index = 1;
+ let mut index = 0;
while index < args.len() {
match args[index].as_str() {
"--ref" => {
- let Some(value) = required_value(args, &mut index, "--ref") else {
- return Ok(2);
- };
- requested_ref = Some(value);
+ let value = args.get(index + 1).ok_or("missing value for --ref")?;
+ requested_ref = Some(value.clone());
+ index += 2;
}
"--yes" | "-y" => {
yes = true;
index += 1;
}
+ other if other.starts_with('-') || source_arg.is_some() => {
+ return Err(format!("unknown option: {other}"));
+ }
other => {
- eprintln!("unknown option: {other}");
- return Ok(2);
+ source_arg = Some(other);
+ index += 1;
}
}
}
+ let source = GithubPluginSource::parse(source_arg.ok_or(PLUGIN_INSTALL_USAGE)?)?;
+ Ok(PluginInstallArgs {
+ source,
+ requested_ref,
+ yes,
+ })
+}
+
+fn plugin_install(args: &[String]) -> std::io::Result<i32> {
+ let PluginInstallArgs {
+ source,
+ requested_ref,
+ yes,
+ } = match parse_plugin_install_args(args) {
+ Ok(args) => args,
+ Err(err) => {
+ eprintln!("{err}");
+ return Ok(2);
+ }
+ };
if !yes && !io::stdin().is_terminal() {
eprintln!("remote plugin install requires --yes when stdin is not interactive");
@@ -737,7 +758,7 @@ impl GithubPluginSource {
}
let parts = value.split('/').collect::<Vec<_>>();
if parts.len() < 2 {
- return Err("usage: herdr plugin install <owner>/<repo>[/subdir...]".into());
+ return Err(PLUGIN_INSTALL_USAGE.into());
}
let owner = parts[0];
let repo = parts[1];
@@ -1738,6 +1759,68 @@ mod tests {
}
#[test]
+ fn plugin_install_args_accept_options_around_source() {
+ for (args, expected_ref, expected_yes) in [
+ (vec!["owner/repo"], None, false),
+ (vec!["--yes", "owner/repo"], None, true),
+ (vec!["-y", "owner/repo"], None, true),
+ (
+ vec!["owner/repo", "--ref", "main", "-y"],
+ Some("main"),
+ true,
+ ),
+ (
+ vec!["--ref", "main", "owner/repo", "--yes"],
+ Some("main"),
+ true,
+ ),
+ (
+ vec![
+ "--yes",
+ "--ref",
+ "old",
+ "owner/repo",
+ "--ref",
+ "main",
+ "--yes",
+ ],
+ Some("main"),
+ true,
+ ),
+ // Preserve ref-value consumption: a value named --yes is not consent.
+ (vec!["owner/repo", "--ref", "--yes"], Some("--yes"), false),
+ ] {
+ let args = args.into_iter().map(String::from).collect::<Vec<_>>();
+ let parsed = parse_plugin_install_args(&args).unwrap();
+ assert_eq!(parsed.source.display(), "owner/repo", "{args:?}");
+ assert_eq!(parsed.requested_ref.as_deref(), expected_ref, "{args:?}");
+ assert_eq!(parsed.yes, expected_yes, "{args:?}");
+ }
+ }
+
+ #[test]
+ fn plugin_install_args_reject_invalid_syntax() {
+ for (args, expected_error) in [
+ (vec![], PLUGIN_INSTALL_USAGE),
+ (vec!["--yes"], PLUGIN_INSTALL_USAGE),
+ (vec!["owner/repo", "--ref"], "missing value for --ref"),
+ (vec!["--unknown", "owner/repo"], "unknown option: --unknown"),
+ (
+ vec!["owner/repo", "extra/repo"],
+ "unknown option: extra/repo",
+ ),
+ (vec!["owner"], PLUGIN_INSTALL_USAGE),
+ ] {
+ let args = args.into_iter().map(String::from).collect::<Vec<_>>();
+ assert_eq!(
+ parse_plugin_install_args(&args).unwrap_err(),
+ expected_error,
+ "{args:?}"
+ );
+ }
+ }
+
+ #[test]
fn github_plugin_source_parses_root_repo() {
let source = GithubPluginSource::parse("ogulcancelik/herdr-plugin-examples").unwrap();
assert_eq!(source.owner, "ogulcancelik");