use kevy_resp::{ArgvView, CmdError, encode_error, encode_integer};
use kevy_store::Store;
use crate::cmd::{arg_f64, store_err};
use super::parse_unit;
use super::search;
use super::search::{Anchor, LegacyRadiusParsed, RadiusReply, SearchError};
pub(super) fn cmd_georadius<A: ArgvView + ?Sized>(
store: &mut Store,
args: &A,
out: &mut Vec<u8>,
read_only: bool,
) {
run_radius(store, out, plan_radius(args, false), read_only);
}
pub(super) fn cmd_georadiusbymember<A: ArgvView + ?Sized>(
store: &mut Store,
args: &A,
out: &mut Vec<u8>,
read_only: bool,
) {
run_radius(store, out, plan_radius(args, true), read_only);
}
pub(super) fn plan_radius<A: ArgvView + ?Sized>(
args: &A,
bymember: bool,
) -> Result<(Vec<u8>, LegacyRadiusParsed), CmdError> {
let (anchor, radius_idx) = if bymember {
if args.len() < 5 {
return Err(CmdError::Wire(
"ERR wrong number of arguments for 'georadiusbymember' command",
));
}
(Anchor::Member(args[2].to_vec()), 3)
} else {
if args.len() < 6 {
return Err(CmdError::Wire("ERR wrong number of arguments for 'georadius' command"));
}
let lon = arg_f64(&args[2]).ok_or("ERR value is not a valid float")?;
let lat = arg_f64(&args[3]).ok_or("ERR value is not a valid float")?;
(Anchor::LonLat(lon, lat), 4)
};
let radius = arg_f64(&args[radius_idx]).ok_or("ERR value is not a valid float")?;
let unit = parse_unit(&args[radius_idx + 1])
.ok_or("ERR unsupported unit provided. please use M, KM, FT, MI")?;
let parsed = search::parse_legacy_radius(args, radius_idx + 2, anchor, radius * unit, unit)?;
Ok((args[1].to_vec(), parsed))
}
fn run_radius(
store: &mut Store,
out: &mut Vec<u8>,
planned: Result<(Vec<u8>, LegacyRadiusParsed), CmdError>,
read_only: bool,
) {
let (key, parsed) = match planned {
Ok(p) => p,
Err(msg) => return encode_error(out, msg.as_wire()),
};
if read_only && parsed.store_dst.is_some() {
return encode_error(out, "ERR can't store result in the _RO variant");
}
let hits = match search::run_search(store, &key, &parsed.opts) {
Ok(h) => h,
Err(SearchError::NoMember) => {
return encode_error(out, "ERR could not decode requested zset member");
}
Err(SearchError::Store(e)) => return store_err(out, e),
};
match search::emit_or_store(out, store, &hits, &parsed) {
RadiusReply::Replied => {}
RadiusReply::Stored(n) => encode_integer(out, n as i64),
}
}
pub(super) fn legacy_store_dst<A: ArgvView + ?Sized>(args: &A, start: usize) -> Option<Vec<u8>> {
let mut dst = None;
let mut i = start;
while i < args.len() {
let step = match args[i].to_ascii_uppercase().as_slice() {
b"STORE" | b"STOREDIST" => {
dst = Some(args.get(i + 1)?.to_vec());
2
}
b"ASC" | b"DESC" | b"WITHCOORD" | b"WITHDIST" | b"WITHHASH" => 1,
b"COUNT" => {
if args.get(i + 2).is_some_and(|a| a.eq_ignore_ascii_case(b"ANY")) {
3
} else {
2
}
}
_ => return None,
};
i += step;
}
dst
}