fred 10.1.0

An async client for Redis and Valkey.
Documentation
use super::*;
use crate::{
  error::Error,
  protocol::{command::CommandKind, utils as protocol_utils},
  types::{geo::*, Any, Key, MultipleValues, SetOptions, SortOrder, Value},
  utils,
};
use std::convert::TryInto;

static WITH_COORD: &str = "WITHCOORD";
static WITH_DIST: &str = "WITHDIST";
static WITH_HASH: &str = "WITHHASH";
static STORE_DIST: &str = "STOREDIST";
static FROM_MEMBER: &str = "FROMMEMBER";
static FROM_LONLAT: &str = "FROMLONLAT";
static BY_RADIUS: &str = "BYRADIUS";
static BY_BOX: &str = "BYBOX";

pub async fn geoadd<C: ClientLike>(
  client: &C,
  key: Key,
  options: Option<SetOptions>,
  changed: bool,
  values: MultipleGeoValues,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(3 + (values.len() * 3));
    args.push(key.into());

    if let Some(options) = options {
      args.push(options.to_str().into());
    }
    if changed {
      args.push(static_val!(CHANGED));
    }

    for value in values.inner().into_iter() {
      args.push(value.coordinates.longitude.try_into()?);
      args.push(value.coordinates.latitude.try_into()?);
      args.push(value.member)
    }

    Ok((CommandKind::GeoAdd, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn geohash<C: ClientLike>(client: &C, key: Key, members: MultipleValues) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let members = members.into_multiple_values();
    let mut args = Vec::with_capacity(1 + members.len());
    args.push(key.into());

    for member in members.into_iter() {
      args.push(member);
    }

    Ok((CommandKind::GeoHash, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn geopos<C: ClientLike>(client: &C, key: Key, members: MultipleValues) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let members = members.into_multiple_values();
    let mut args = Vec::with_capacity(1 + members.len());
    args.push(key.into());

    for member in members.into_iter() {
      args.push(member);
    }

    Ok((CommandKind::GeoPos, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn geodist<C: ClientLike>(
  client: &C,
  key: Key,
  src: Value,
  dest: Value,
  unit: Option<GeoUnit>,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(4);
    args.push(key.into());
    args.push(src);
    args.push(dest);

    if let Some(unit) = unit {
      args.push(unit.to_str().into());
    }

    Ok((CommandKind::GeoDist, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn georadius<C: ClientLike>(
  client: &C,
  key: Key,
  position: GeoPosition,
  radius: f64,
  unit: GeoUnit,
  withcoord: bool,
  withdist: bool,
  withhash: bool,
  count: Option<(u64, Any)>,
  ord: Option<SortOrder>,
  store: Option<Key>,
  storedist: Option<Key>,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(16);
    args.push(key.into());
    args.push(position.longitude.try_into()?);
    args.push(position.latitude.try_into()?);
    args.push(radius.try_into()?);
    args.push(unit.to_str().into());

    if withcoord {
      args.push(static_val!(WITH_COORD));
    }
    if withdist {
      args.push(static_val!(WITH_DIST));
    }
    if withhash {
      args.push(static_val!(WITH_HASH));
    }
    if let Some((count, any)) = count {
      args.push(static_val!(COUNT));
      args.push(count.try_into()?);
      if any {
        args.push(static_val!(ANY));
      }
    }
    if let Some(ord) = ord {
      args.push(ord.to_str().into());
    }
    if let Some(store) = store {
      args.push(static_val!(STORE));
      args.push(store.into());
    }
    if let Some(store_dist) = storedist {
      args.push(static_val!(STORE_DIST));
      args.push(store_dist.into());
    }

    Ok((CommandKind::GeoRadius, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn georadiusbymember<C: ClientLike>(
  client: &C,
  key: Key,
  member: Value,
  radius: f64,
  unit: GeoUnit,
  withcoord: bool,
  withdist: bool,
  withhash: bool,
  count: Option<(u64, Any)>,
  ord: Option<SortOrder>,
  store: Option<Key>,
  storedist: Option<Key>,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(15);
    args.push(key.into());
    args.push(member);
    args.push(radius.try_into()?);
    args.push(unit.to_str().into());

    if withcoord {
      args.push(static_val!(WITH_COORD));
    }
    if withdist {
      args.push(static_val!(WITH_DIST));
    }
    if withhash {
      args.push(static_val!(WITH_HASH));
    }
    if let Some((count, any)) = count {
      args.push(static_val!(COUNT));
      args.push(count.try_into()?);
      if any {
        args.push(static_val!(ANY));
      }
    }
    if let Some(ord) = ord {
      args.push(ord.to_str().into());
    }
    if let Some(store) = store {
      args.push(static_val!(STORE));
      args.push(store.into());
    }
    if let Some(store_dist) = storedist {
      args.push(static_val!(STORE_DIST));
      args.push(store_dist.into());
    }

    Ok((CommandKind::GeoRadiusByMember, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn geosearch<C: ClientLike>(
  client: &C,
  key: Key,
  from_member: Option<Value>,
  from_lonlat: Option<GeoPosition>,
  by_radius: Option<(f64, GeoUnit)>,
  by_box: Option<(f64, f64, GeoUnit)>,
  ord: Option<SortOrder>,
  count: Option<(u64, Any)>,
  withcoord: bool,
  withdist: bool,
  withhash: bool,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(15);
    args.push(key.into());

    if let Some(member) = from_member {
      args.push(static_val!(FROM_MEMBER));
      args.push(member);
    }
    if let Some(position) = from_lonlat {
      args.push(static_val!(FROM_LONLAT));
      args.push(position.longitude.try_into()?);
      args.push(position.latitude.try_into()?);
    }

    if let Some((radius, unit)) = by_radius {
      args.push(static_val!(BY_RADIUS));
      args.push(radius.try_into()?);
      args.push(unit.to_str().into());
    }
    if let Some((width, height, unit)) = by_box {
      args.push(static_val!(BY_BOX));
      args.push(width.try_into()?);
      args.push(height.try_into()?);
      args.push(unit.to_str().into());
    }
    if let Some(ord) = ord {
      args.push(ord.to_str().into());
    }
    if let Some((count, any)) = count {
      args.push(static_val!(COUNT));
      args.push(count.try_into()?);
      if any {
        args.push(static_val!(ANY));
      }
    }
    if withcoord {
      args.push(static_val!(WITH_COORD));
    }
    if withdist {
      args.push(static_val!(WITH_DIST));
    }
    if withhash {
      args.push(static_val!(WITH_HASH));
    }

    Ok((CommandKind::GeoSearch, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}

pub async fn geosearchstore<C: ClientLike>(
  client: &C,
  dest: Key,
  source: Key,
  from_member: Option<Value>,
  from_lonlat: Option<GeoPosition>,
  by_radius: Option<(f64, GeoUnit)>,
  by_box: Option<(f64, f64, GeoUnit)>,
  ord: Option<SortOrder>,
  count: Option<(u64, Any)>,
  storedist: bool,
) -> Result<Value, Error> {
  let frame = utils::request_response(client, move || {
    let mut args = Vec::with_capacity(14);
    args.push(dest.into());
    args.push(source.into());

    if let Some(member) = from_member {
      args.push(static_val!(FROM_MEMBER));
      args.push(member);
    }
    if let Some(position) = from_lonlat {
      args.push(static_val!(FROM_LONLAT));
      args.push(position.longitude.try_into()?);
      args.push(position.latitude.try_into()?);
    }
    if let Some((radius, unit)) = by_radius {
      args.push(static_val!(BY_RADIUS));
      args.push(radius.try_into()?);
      args.push(unit.to_str().into());
    }
    if let Some((width, height, unit)) = by_box {
      args.push(static_val!(BY_BOX));
      args.push(width.try_into()?);
      args.push(height.try_into()?);
      args.push(unit.to_str().into());
    }
    if let Some(ord) = ord {
      args.push(ord.to_str().into());
    }
    if let Some((count, any)) = count {
      args.push(static_val!(COUNT));
      args.push(count.try_into()?);
      if any {
        args.push(static_val!(ANY));
      }
    }
    if storedist {
      args.push(static_val!(STORE_DIST));
    }

    Ok((CommandKind::GeoSearchStore, args))
  })
  .await?;

  protocol_utils::frame_to_results(frame)
}