use kevy_resp::ArgvView;
use kevy_resp::CmdError;
use crate::cmd::arg_f64;
use super::super::parse_unit;
use super::{Anchor, LegacyRadiusParsed, Opts, Shape, Sort};
pub(in crate::dispatch_geo) fn parse_opts<A: ArgvView + ?Sized>(
args: &A,
) -> Result<Opts, CmdError> {
parse_opts_at(args, 2)
}
pub(in crate::dispatch_geo) fn parse_opts_at<A: ArgvView + ?Sized>(
args: &A,
start: usize,
) -> Result<Opts, CmdError> {
let mut state = OptsBuilder::default();
let mut i = start;
while i < args.len() {
let tok = args[i].to_ascii_uppercase();
i += parse_one_opt(args, &tok, i, &mut state)?;
}
state.finish()
}
pub(in crate::dispatch_geo) fn parse_legacy_radius<A: ArgvView + ?Sized>(
args: &A,
start: usize,
anchor: Anchor,
radius_m: f64,
unit: f64,
) -> Result<LegacyRadiusParsed, CmdError> {
let mut s = OptsBuilder {
from: Some(anchor),
shape: Some((Shape::Radius { r_m: radius_m }, unit)),
..OptsBuilder::default()
};
let mut store_dst: Option<Vec<u8>> = None;
let mut i = start;
while i < args.len() {
let tok = args[i].to_ascii_uppercase();
i += match tok.as_slice() {
b"STORE" => {
let dst = args.get(i + 1).ok_or("ERR syntax error")?;
store_dst = Some(dst.to_vec());
s.storedist = false;
2
}
b"STOREDIST" => {
let dst = args.get(i + 1).ok_or("ERR syntax error")?;
store_dst = Some(dst.to_vec());
s.storedist = true;
2
}
_ => parse_one_opt(args, &tok, i, &mut s)?,
};
}
if store_dst.is_some() && (s.with_coord || s.with_dist || s.with_hash) {
return Err(CmdError::Wire(
"ERR STORE option in GEORADIUS is not compatible with WITHCOORD, WITHDIST and WITHHASH options",
));
}
let opts = s.finish()?;
Ok(LegacyRadiusParsed { opts, store_dst })
}
#[derive(Default)]
pub(super) struct OptsBuilder {
pub(super) from: Option<Anchor>,
pub(super) shape: Option<(Shape, f64)>,
pub(super) sort: Sort,
pub(super) count: Option<usize>,
pub(super) any: bool,
pub(super) with_coord: bool,
pub(super) with_dist: bool,
pub(super) with_hash: bool,
pub(super) storedist: bool,
}
impl OptsBuilder {
fn finish(self) -> Result<Opts, CmdError> {
let from = self.from.ok_or("ERR syntax error: missing FROMMEMBER / FROMLONLAT")?;
let (shape, unit) = self.shape.ok_or("ERR syntax error: missing BYRADIUS / BYBOX")?;
Ok(Opts {
from,
shape,
unit,
sort: self.sort,
count: self.count,
any: self.any,
with_coord: self.with_coord,
with_dist: self.with_dist,
with_hash: self.with_hash,
storedist: self.storedist,
})
}
}
fn parse_one_opt<A: ArgvView + ?Sized>(
args: &A,
tok: &[u8],
i: usize,
s: &mut OptsBuilder,
) -> Result<usize, CmdError> {
match tok {
b"FROMMEMBER" | b"FROMLONLAT" => parse_from(args, tok, i, &mut s.from),
b"BYRADIUS" | b"BYBOX" => parse_shape(args, tok, i, &mut s.shape),
b"ASC" => {
s.sort = Sort::Asc;
Ok(1)
}
b"DESC" => {
s.sort = Sort::Desc;
Ok(1)
}
b"COUNT" => parse_count(args, i, &mut s.count, &mut s.any),
b"WITHCOORD" => {
s.with_coord = true;
Ok(1)
}
b"WITHDIST" => {
s.with_dist = true;
Ok(1)
}
b"WITHHASH" => {
s.with_hash = true;
Ok(1)
}
b"STOREDIST" => {
s.storedist = true;
Ok(1)
}
_ => Err(CmdError::Wire("ERR syntax error")),
}
}
fn parse_from<A: ArgvView + ?Sized>(
args: &A,
tok: &[u8],
i: usize,
from: &mut Option<Anchor>,
) -> Result<usize, CmdError> {
if tok == b"FROMMEMBER" {
let m = args.get(i + 1).ok_or("ERR syntax error")?;
*from = Some(Anchor::Member(m.to_vec()));
return Ok(2);
}
let lon = arg_f64(args.get(i + 1).ok_or("ERR syntax error")?)
.ok_or("ERR value is not a valid float")?;
let lat = arg_f64(args.get(i + 2).ok_or("ERR syntax error")?)
.ok_or("ERR value is not a valid float")?;
*from = Some(Anchor::LonLat(lon, lat));
Ok(3)
}
fn parse_shape<A: ArgvView + ?Sized>(
args: &A,
tok: &[u8],
i: usize,
shape: &mut Option<(Shape, f64)>,
) -> Result<usize, CmdError> {
if tok == b"BYRADIUS" {
let r = arg_f64(args.get(i + 1).ok_or("ERR syntax error")?)
.ok_or("ERR value is not a valid float")?;
let u = parse_unit(args.get(i + 2).ok_or("ERR syntax error")?)
.ok_or("ERR unsupported unit provided. please use M, KM, FT, MI")?;
*shape = Some((Shape::Radius { r_m: r * u }, u));
return Ok(3);
}
let w = arg_f64(args.get(i + 1).ok_or("ERR syntax error")?)
.ok_or("ERR value is not a valid float")?;
let h = arg_f64(args.get(i + 2).ok_or("ERR syntax error")?)
.ok_or("ERR value is not a valid float")?;
let u = parse_unit(args.get(i + 3).ok_or("ERR syntax error")?)
.ok_or("ERR unsupported unit provided. please use M, KM, FT, MI")?;
*shape = Some((Shape::Box { w_m: w * u, h_m: h * u }, u));
Ok(4)
}
fn parse_count<A: ArgvView + ?Sized>(
args: &A,
i: usize,
count: &mut Option<usize>,
any: &mut bool,
) -> Result<usize, CmdError> {
let n: i64 = std::str::from_utf8(args.get(i + 1).ok_or("ERR syntax error")?)
.ok()
.and_then(|s| s.parse().ok())
.ok_or("ERR value is not an integer or out of range")?;
if n <= 0 {
return Err(CmdError::Wire("ERR COUNT can't be negative"));
}
*count = Some(n as usize);
if let Some(next) = args.get(i + 2)
&& next.eq_ignore_ascii_case(b"ANY")
{
*any = true;
return Ok(3);
}
Ok(2)
}