use kevy_resp::{Argv, ArgvView, CmdError, encode_error};
use kevy_rt::{GeoHits, Route};
use kevy_store::Store;
use crate::cmd::store_err;
use super::radius::{legacy_store_dst, plan_radius};
use super::search::{Opts, SearchError, plan_geosearchstore, search_pairs};
pub(crate) fn geo_store_route<A: ArgvView + ?Sized>(verb: &[u8], args: &A) -> Option<Route> {
match verb {
b"GEOSEARCHSTORE" if args.len() >= 5 => {
Some(Route::GeoStore { dst: args[1].to_vec(), src: args[2].to_vec() })
}
b"GEORADIUS" if args.len() >= 6 => {
legacy_store_dst(args, 6).map(|dst| Route::GeoStore { src: args[1].to_vec(), dst })
}
b"GEORADIUSBYMEMBER" if args.len() >= 5 => {
legacy_store_dst(args, 5).map(|dst| Route::GeoStore { src: args[1].to_vec(), dst })
}
_ => None,
}
}
pub(crate) fn geo_search(store: &mut Store, argv: &[Vec<u8>]) -> GeoHits {
let mut args = Argv::with_capacity(argv.len(), 0);
for a in argv {
args.push(a);
}
let planned = match verb_of(argv).as_slice() {
b"GEOSEARCHSTORE" => plan_geosearchstore(&args),
b"GEORADIUS" => plan_radius(&args, false).map(|(src, p)| (src, p.opts)),
b"GEORADIUSBYMEMBER" => plan_radius(&args, true).map(|(src, p)| (src, p.opts)),
_ => Err(CmdError::Wire("ERR unknown command")),
};
match planned {
Ok((src, opts)) => run(store, &src, &opts),
Err(msg) => GeoHits::Error(encoded(|out| encode_error(out, msg.as_wire()))),
}
}
fn run(store: &mut Store, src: &[u8], opts: &Opts) -> GeoHits {
match search_pairs(store, src, opts) {
Ok(pairs) => GeoHits::Pairs(pairs),
Err(SearchError::NoMember) => GeoHits::Error(encoded(|out| {
encode_error(out, "ERR could not decode requested zset member");
})),
Err(SearchError::Store(e)) => GeoHits::Error(encoded(|out| store_err(out, e))),
}
}
fn verb_of(argv: &[Vec<u8>]) -> Vec<u8> {
argv.first().map(|v| v.to_ascii_uppercase()).unwrap_or_default()
}
fn encoded(f: impl FnOnce(&mut Vec<u8>)) -> Vec<u8> {
let mut out = Vec::new();
f(&mut out);
out
}