commit 73a4cfc6dc523046d53d585fb188eb04c44ced8d
parent 50d64a827851c912d8e309170ceca3209ab971c9
Author: triesap <tyson@radroots.org>
Date: Fri, 26 Jun 2026 23:31:39 +0000
cli: add canonical farm location modes
- replace the farm location lookup flag with coordinate, city, query, and GeoNames id modes
- add local-only location labels to target input translation
- cover parser rejection for mixed modes, incomplete coordinates, and removed lookup input
Diffstat:
4 files changed, 156 insertions(+), 8 deletions(-)
diff --git a/src/cli/farm.rs b/src/cli/farm.rs
@@ -1,4 +1,4 @@
-use clap::{Args, Subcommand};
+use clap::{ArgGroup, Args, Subcommand};
#[derive(Debug, Clone, Args)]
pub struct FarmArgs {
@@ -86,15 +86,31 @@ pub enum FarmLocationCommand {
}
#[derive(Debug, Clone, Args)]
+#[command(group(
+ ArgGroup::new("location_mode")
+ .args(["lat", "city", "query", "geonames_id"])
+ .required(true)
+ .multiple(false)
+))]
pub struct FarmLocationSetArgs {
- #[arg(long, allow_negative_numbers = true)]
+ #[arg(long, allow_negative_numbers = true, requires = "lng")]
pub lat: Option<f64>,
- #[arg(long, allow_negative_numbers = true)]
+ #[arg(long, allow_negative_numbers = true, requires = "lat")]
pub lng: Option<f64>,
#[arg(long = "farm-d-tag")]
pub farm_d_tag: Option<String>,
- #[arg(long = "lookup", default_value = "geonames")]
- pub lookup: String,
+ #[arg(long)]
+ pub city: Option<String>,
+ #[arg(long, requires = "city", conflicts_with_all = ["lat", "query", "geonames_id"])]
+ pub region: Option<String>,
+ #[arg(long, requires = "city", conflicts_with_all = ["lat", "query", "geonames_id"])]
+ pub country: Option<String>,
+ #[arg(long)]
+ pub query: Option<String>,
+ #[arg(long = "geonames-id", value_parser = clap::value_parser!(i64).range(1..))]
+ pub geonames_id: Option<i64>,
+ #[arg(long)]
+ pub label: Option<String>,
}
#[derive(Debug, Clone, Args)]
diff --git a/src/cli/input.rs b/src/cli/input.rs
@@ -111,7 +111,17 @@ pub fn target_operation_input(command: &TargetCommand) -> OperationData {
insert_number(&mut input, "longitude", longitude);
}
insert_string(&mut input, "farm_d_tag", &args.farm_d_tag);
- input.insert("lookup".to_owned(), Value::String(args.lookup.clone()));
+ insert_string(&mut input, "city", &args.city);
+ insert_string(&mut input, "region", &args.region);
+ insert_string(&mut input, "country", &args.country);
+ insert_string(&mut input, "query", &args.query);
+ if let Some(geonames_id) = args.geonames_id {
+ input.insert(
+ "geonames_id".to_owned(),
+ Value::Number(serde_json::Number::from(geonames_id)),
+ );
+ }
+ insert_string(&mut input, "label", &args.label);
}
FarmLocationCommand::Get(args) | FarmLocationCommand::Clear(args) => {
insert_string(&mut input, "farm_d_tag", &args.farm_d_tag);
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
@@ -461,6 +461,128 @@ mod tests {
}
#[test]
+ fn target_parser_accepts_structured_farm_location_city() {
+ let parsed = TargetCliArgs::try_parse_from([
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--city",
+ "Victoria",
+ "--region",
+ "BC",
+ "--country",
+ "CA",
+ "--label",
+ "main pickup point",
+ ])
+ .expect("target args parse");
+
+ let crate::cli::TargetCommand::Farm(farm) = parsed.command else {
+ panic!("expected farm command")
+ };
+ let FarmCommand::Location(location) = farm.command else {
+ panic!("expected farm location command")
+ };
+ let FarmLocationCommand::Set(args) = location.command else {
+ panic!("expected farm location set command")
+ };
+ assert_eq!(args.city.as_deref(), Some("Victoria"));
+ assert_eq!(args.region.as_deref(), Some("BC"));
+ assert_eq!(args.country.as_deref(), Some("CA"));
+ assert_eq!(args.label.as_deref(), Some("main pickup point"));
+ }
+
+ #[test]
+ fn target_parser_accepts_query_and_geonames_id_farm_location_modes() {
+ let query = TargetCliArgs::try_parse_from([
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--query",
+ "Fixture Victoria, BC, CA",
+ ])
+ .expect("query args parse");
+ let crate::cli::TargetCommand::Farm(farm) = query.command else {
+ panic!("expected farm command")
+ };
+ let FarmCommand::Location(location) = farm.command else {
+ panic!("expected farm location command")
+ };
+ let FarmLocationCommand::Set(args) = location.command else {
+ panic!("expected farm location set command")
+ };
+ assert_eq!(args.query.as_deref(), Some("Fixture Victoria, BC, CA"));
+
+ let geonames_id = TargetCliArgs::try_parse_from([
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--geonames-id",
+ "3004",
+ ])
+ .expect("geonames id args parse");
+ let crate::cli::TargetCommand::Farm(farm) = geonames_id.command else {
+ panic!("expected farm command")
+ };
+ let FarmCommand::Location(location) = farm.command else {
+ panic!("expected farm location command")
+ };
+ let FarmLocationCommand::Set(args) = location.command else {
+ panic!("expected farm location set command")
+ };
+ assert_eq!(args.geonames_id, Some(3004));
+ }
+
+ #[test]
+ fn target_parser_rejects_invalid_farm_location_modes() {
+ for argv in [
+ vec!["radroots", "farm", "location", "set", "--lat", "48.429456"],
+ vec![
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--lat",
+ "48.429456",
+ "--lng",
+ "-123.349786",
+ "--city",
+ "Victoria",
+ ],
+ vec![
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--query",
+ "Victoria, BC",
+ "--country",
+ "CA",
+ ],
+ vec![
+ "radroots",
+ "farm",
+ "location",
+ "set",
+ "--lookup",
+ "geonames",
+ "--lat",
+ "48.429456",
+ "--lng",
+ "-123.349786",
+ ],
+ ] {
+ assert!(
+ TargetCliArgs::try_parse_from(argv).is_err(),
+ "expected farm location args to fail"
+ );
+ }
+ }
+
+ #[test]
fn target_parser_accepts_listing_rebind_inputs() {
let parsed = TargetCliArgs::try_parse_from([
"radroots",
diff --git a/src/registry/farm.rs b/src/registry/farm.rs
@@ -66,12 +66,12 @@ pub const FARM_PROFILE_UPDATE: OperationSpec = operation!(
pub const FARM_LOCATION_SET: OperationSpec = operation!(
"farm.location.set",
- "radroots farm location set",
+ "radroots farm location set --lat 48.429456 --lng -123.349786",
"farm",
"farm_location_set",
"FarmLocationSetRequest",
"FarmLocationSetResult",
- "Set private exact farm location and derived public locality.",
+ "Set private farm location and derived public locality.",
Seller,
true,
Conditional,