use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::result::Result as StdResult;
use std::{f32, f64};
use byteorder::{ByteOrder, NetworkEndian};
use ripemd160::digest::Digest;
use ripemd160::Ripemd160;
use std::vec::Vec;
use crate::commands::buffer::Buffer;
use crate::commands::ParticleType;
use crate::errors::Result;
use crate::msgpack::{decoder, encoder};
#[cfg(feature = "serialization")]
use serde::ser::{SerializeMap, SerializeSeq};
#[cfg(feature = "serialization")]
use serde::{Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FloatValue {
F32(u32),
F64(u64),
}
impl From<FloatValue> for f64 {
fn from(val: FloatValue) -> f64 {
match val {
FloatValue::F32(_) => panic!(
"This library does not automatically convert f32 -> f64 to be used in keys \
or bins."
),
FloatValue::F64(val) => f64::from_bits(val),
}
}
}
impl<'a> From<&'a FloatValue> for f64 {
fn from(val: &FloatValue) -> f64 {
match *val {
FloatValue::F32(_) => panic!(
"This library does not automatically convert f32 -> f64 to be used in keys \
or bins."
),
FloatValue::F64(val) => f64::from_bits(val),
}
}
}
impl From<f64> for FloatValue {
fn from(val: f64) -> FloatValue {
let mut val = val;
if val.is_nan() {
val = f64::NAN
} FloatValue::F64(val.to_bits())
}
}
impl<'a> From<&'a f64> for FloatValue {
fn from(val: &f64) -> FloatValue {
let mut val = *val;
if val.is_nan() {
val = f64::NAN
} FloatValue::F64(val.to_bits())
}
}
impl From<FloatValue> for f32 {
fn from(val: FloatValue) -> f32 {
match val {
FloatValue::F32(val) => f32::from_bits(val),
FloatValue::F64(val) => f32::from_bits(val as u32),
}
}
}
impl<'a> From<&'a FloatValue> for f32 {
fn from(val: &FloatValue) -> f32 {
match *val {
FloatValue::F32(val) => f32::from_bits(val),
FloatValue::F64(val) => f32::from_bits(val as u32),
}
}
}
impl From<f32> for FloatValue {
fn from(val: f32) -> FloatValue {
let mut val = val;
if val.is_nan() {
val = f32::NAN
} FloatValue::F32(val.to_bits())
}
}
impl<'a> From<&'a f32> for FloatValue {
fn from(val: &f32) -> FloatValue {
let mut val = *val;
if val.is_nan() {
val = f32::NAN
} FloatValue::F32(val.to_bits())
}
}
impl fmt::Display for FloatValue {
fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
match *self {
FloatValue::F32(val) => {
let val: f32 = f32::from_bits(val);
write!(f, "{}", val)
}
FloatValue::F64(val) => {
let val: f64 = f64::from_bits(val);
write!(f, "{}", val)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
Nil,
Bool(bool),
Int(i64),
UInt(u64),
Float(FloatValue),
String(String),
Blob(Vec<u8>),
List(Vec<Value>),
HashMap(HashMap<Value, Value>),
OrderedMap(Vec<(Value, Value)>),
GeoJSON(String),
HLL(Vec<u8>),
}
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
match *self {
Value::Nil => {
let v: Option<u8> = None;
v.hash(state)
}
Value::Bool(ref val) => val.hash(state),
Value::Int(ref val) => val.hash(state),
Value::UInt(ref val) => val.hash(state),
Value::Float(ref val) => val.hash(state),
Value::String(ref val) | Value::GeoJSON(ref val) => val.hash(state),
Value::Blob(ref val) | Value::HLL(ref val) => val.hash(state),
Value::List(ref val) => val.hash(state),
Value::HashMap(_) => panic!("HashMaps cannot be used as map keys."),
Value::OrderedMap(_) => panic!("OrderedMaps cannot be used as map keys."),
}
}
}
impl Value {
pub const fn is_nil(&self) -> bool {
matches!(*self, Value::Nil)
}
#[doc(hidden)]
pub fn particle_type(&self) -> ParticleType {
match *self {
Value::Nil => ParticleType::NULL,
Value::Int(_) => ParticleType::INTEGER,
Value::UInt(_) => panic!(
"Aerospike does not support u64 natively on server-side. Use casting to \
store and retrieve u64 values."
),
Value::Float(_) => ParticleType::FLOAT,
Value::String(_) => ParticleType::STRING,
Value::Blob(_) => ParticleType::BLOB,
Value::Bool(_) => ParticleType::BOOL,
Value::List(_) => ParticleType::LIST,
Value::HashMap(_) => ParticleType::MAP,
Value::OrderedMap(_) => panic!("The library never passes ordered maps to the server."),
Value::GeoJSON(_) => ParticleType::GEOJSON,
Value::HLL(_) => ParticleType::HLL,
}
}
pub fn as_string(&self) -> String {
match *self {
Value::Nil => "<null>".to_string(),
Value::Int(ref val) => val.to_string(),
Value::UInt(ref val) => val.to_string(),
Value::Bool(ref val) => val.to_string(),
Value::Float(ref val) => val.to_string(),
Value::String(ref val) | Value::GeoJSON(ref val) => val.to_string(),
Value::Blob(ref val) | Value::HLL(ref val) => format!("{:?}", val),
Value::List(ref val) => format!("{:?}", val),
Value::HashMap(ref val) => format!("{:?}", val),
Value::OrderedMap(ref val) => format!("{:?}", val),
}
}
#[doc(hidden)]
pub fn estimate_size(&self) -> Result<usize> {
match *self {
Value::Nil => Ok(0),
Value::Int(_) | Value::Float(_) => Ok(8),
Value::UInt(_) => panic!(
"Aerospike does not support u64 natively on server-side. Use casting to \
store and retrieve u64 values."
),
Value::String(ref s) => Ok(s.len()),
Value::Blob(ref b) => Ok(b.len()),
Value::Bool(_) => Ok(1),
Value::List(_) | Value::HashMap(_) => encoder::pack_value(&mut None, self),
Value::OrderedMap(_) => panic!("The library never passes ordered maps to the server."),
Value::GeoJSON(ref s) => Ok(1 + 2 + s.len()), Value::HLL(ref h) => Ok(h.len()),
}
}
#[doc(hidden)]
pub fn write_to(&self, buf: &mut Buffer) -> Result<usize> {
match *self {
Value::Nil => Ok(0),
Value::Int(ref val) => buf.write_i64(*val),
Value::UInt(_) => panic!(
"Aerospike does not support u64 natively on server-side. Use casting to \
store and retrieve u64 values."
),
Value::Bool(ref val) => buf.write_bool(*val),
Value::Float(ref val) => buf.write_f64(f64::from(val)),
Value::String(ref val) => buf.write_str(val),
Value::Blob(ref val) | Value::HLL(ref val) => buf.write_bytes(val),
Value::List(_) | Value::HashMap(_) => encoder::pack_value(&mut Some(buf), self),
Value::OrderedMap(_) => panic!("The library never passes ordered maps to the server."),
Value::GeoJSON(ref val) => buf.write_geo(val),
}
}
#[doc(hidden)]
pub fn write_key_bytes(&self, h: &mut Ripemd160) -> Result<()> {
match *self {
Value::Int(ref val) => {
let mut buf = [0; 8];
NetworkEndian::write_i64(&mut buf, *val);
h.input(&buf);
Ok(())
}
Value::String(ref val) => {
h.input(val.as_bytes());
Ok(())
}
Value::Blob(ref val) => {
h.input(val);
Ok(())
}
_ => panic!("Data type is not supported as Key value."),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> StdResult<(), fmt::Error> {
write!(f, "{}", self.as_string())
}
}
impl From<String> for Value {
fn from(val: String) -> Value {
Value::String(val)
}
}
impl From<Vec<u8>> for Value {
fn from(val: Vec<u8>) -> Value {
Value::Blob(val)
}
}
impl From<Vec<Value>> for Value {
fn from(val: Vec<Value>) -> Value {
Value::List(val)
}
}
impl From<HashMap<Value, Value>> for Value {
fn from(val: HashMap<Value, Value>) -> Value {
Value::HashMap(val)
}
}
impl From<f32> for Value {
fn from(val: f32) -> Value {
Value::Float(FloatValue::from(val))
}
}
impl From<f64> for Value {
fn from(val: f64) -> Value {
Value::Float(FloatValue::from(val))
}
}
impl<'a> From<&'a f32> for Value {
fn from(val: &'a f32) -> Value {
Value::Float(FloatValue::from(*val))
}
}
impl<'a> From<&'a f64> for Value {
fn from(val: &'a f64) -> Value {
Value::Float(FloatValue::from(*val))
}
}
impl<'a> From<&'a String> for Value {
fn from(val: &'a String) -> Value {
Value::String(val.clone())
}
}
impl<'a> From<&'a str> for Value {
fn from(val: &'a str) -> Value {
Value::String(val.to_string())
}
}
impl<'a> From<&'a Vec<u8>> for Value {
fn from(val: &'a Vec<u8>) -> Value {
Value::Blob(val.clone())
}
}
impl<'a> From<&'a [u8]> for Value {
fn from(val: &'a [u8]) -> Value {
Value::Blob(val.to_vec())
}
}
impl From<bool> for Value {
fn from(val: bool) -> Value {
Value::Bool(val)
}
}
impl From<i8> for Value {
fn from(val: i8) -> Value {
Value::Int(i64::from(val))
}
}
impl From<u8> for Value {
fn from(val: u8) -> Value {
Value::Int(i64::from(val))
}
}
impl From<i16> for Value {
fn from(val: i16) -> Value {
Value::Int(i64::from(val))
}
}
impl From<u16> for Value {
fn from(val: u16) -> Value {
Value::Int(i64::from(val))
}
}
impl From<i32> for Value {
fn from(val: i32) -> Value {
Value::Int(i64::from(val))
}
}
impl From<u32> for Value {
fn from(val: u32) -> Value {
Value::Int(i64::from(val))
}
}
impl From<i64> for Value {
fn from(val: i64) -> Value {
Value::Int(val)
}
}
impl From<u64> for Value {
fn from(val: u64) -> Value {
Value::UInt(val)
}
}
impl From<isize> for Value {
fn from(val: isize) -> Value {
Value::Int(val as i64)
}
}
impl From<usize> for Value {
fn from(val: usize) -> Value {
Value::UInt(val as u64)
}
}
impl<'a> From<&'a i8> for Value {
fn from(val: &'a i8) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a u8> for Value {
fn from(val: &'a u8) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a i16> for Value {
fn from(val: &'a i16) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a u16> for Value {
fn from(val: &'a u16) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a i32> for Value {
fn from(val: &'a i32) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a u32> for Value {
fn from(val: &'a u32) -> Value {
Value::Int(i64::from(*val))
}
}
impl<'a> From<&'a i64> for Value {
fn from(val: &'a i64) -> Value {
Value::Int(*val)
}
}
impl<'a> From<&'a u64> for Value {
fn from(val: &'a u64) -> Value {
Value::UInt(*val)
}
}
impl<'a> From<&'a isize> for Value {
fn from(val: &'a isize) -> Value {
Value::Int(*val as i64)
}
}
impl<'a> From<&'a usize> for Value {
fn from(val: &'a usize) -> Value {
Value::UInt(*val as u64)
}
}
impl<'a> From<&'a bool> for Value {
fn from(val: &'a bool) -> Value {
Value::Bool(*val)
}
}
impl From<Value> for i64 {
fn from(val: Value) -> i64 {
match val {
Value::Int(val) => val,
Value::UInt(val) => val as i64,
_ => panic!("Value is not an integer to convert."),
}
}
}
impl<'a> From<&'a Value> for i64 {
fn from(val: &'a Value) -> i64 {
match *val {
Value::Int(val) => val,
Value::UInt(val) => val as i64,
_ => panic!("Value is not an integer to convert."),
}
}
}
impl TryFrom<Value> for String {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::String(v) => Ok(v),
Value::GeoJSON(v) => Ok(v),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for Vec<u8> {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::Blob(v) => Ok(v),
Value::HLL(v) => Ok(v),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for Vec<Value> {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::List(v) => Ok(v),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for HashMap<Value, Value> {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::HashMap(v) => Ok(v),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for Vec<(Value, Value)> {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::OrderedMap(v) => Ok(v),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for f64 {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::Float(v) => Ok(f64::from(v)),
_ => bail!(format!(
"Invalid type conversion from Value::{} to {}",
val.particle_type(),
std::any::type_name::<Self>()
)),
}
}
}
impl TryFrom<Value> for bool {
type Error = String;
fn try_from(val: Value) -> std::result::Result<Self, Self::Error> {
match val {
Value::Bool(v) => Ok(v),
_ => bail!("Invalid type bool"),
}
}
}
#[doc(hidden)]
pub fn bytes_to_particle(ptype: u8, buf: &mut Buffer, len: usize) -> Result<Value> {
match ParticleType::from(ptype) {
ParticleType::NULL => Ok(Value::Nil),
ParticleType::INTEGER => {
let val = buf.read_i64(None)?;
Ok(Value::Int(val))
}
ParticleType::FLOAT => {
let val = buf.read_f64(None)?;
Ok(Value::Float(FloatValue::from(val)))
}
ParticleType::STRING => {
let val = buf.read_str(len)?;
Ok(Value::String(val))
}
ParticleType::GEOJSON => {
buf.skip(1)?;
let ncells = buf.read_i16(None)? as usize;
let header_size: usize = ncells * 8;
buf.skip(header_size)?;
let val = buf.read_str(len - header_size - 3)?;
Ok(Value::GeoJSON(val))
}
ParticleType::BLOB => Ok(Value::Blob(buf.read_blob(len)?)),
ParticleType::LIST => {
let val = decoder::unpack_value_list(buf)?;
Ok(val)
}
ParticleType::MAP => {
let val = decoder::unpack_value_map(buf)?;
Ok(val)
}
ParticleType::DIGEST => Ok(Value::from("A DIGEST, NOT IMPLEMENTED YET!")),
ParticleType::LDT => Ok(Value::from("A LDT, NOT IMPLEMENTED YET!")),
ParticleType::HLL => Ok(Value::HLL(buf.read_blob(len)?)),
ParticleType::BOOL => Ok(Value::Bool(buf.read_bool(len)?)),
}
}
#[macro_export]
macro_rules! as_val {
($val:expr) => {{
$crate::Value::from($val)
}};
}
#[macro_export]
macro_rules! as_geo {
($val:expr) => {{
$crate::Value::GeoJSON($val.to_owned())
}};
}
#[macro_export]
macro_rules! as_blob {
($val:expr) => {{
$crate::Value::Blob($val)
}};
}
#[macro_export]
macro_rules! as_list {
( $( $v:expr),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push(as_val!($v));
)*
$crate::Value::List(temp_vec)
}
};
}
#[macro_export]
macro_rules! as_values {
( $( $v:expr),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push(as_val!($v));
)*
temp_vec
}
};
}
#[macro_export]
macro_rules! as_map {
( $( $k:expr => $v:expr),* ) => {
{
let mut temp_map = std::collections::HashMap::new();
$(
temp_map.insert(as_val!($k), as_val!($v));
)*
$crate::Value::HashMap(temp_map)
}
};
}
#[cfg(feature = "serialization")]
impl Serialize for Value {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
match &self {
Value::Nil => serializer.serialize_none(),
Value::Bool(b) => serializer.serialize_bool(*b),
Value::Int(i) => serializer.serialize_i64(*i),
Value::UInt(u) => serializer.serialize_u64(*u),
Value::Float(f) => match f {
FloatValue::F32(u) => serializer.serialize_f32(f32::from_bits(*u)),
FloatValue::F64(u) => serializer.serialize_f64(f64::from_bits(*u)),
},
Value::String(s) | Value::GeoJSON(s) => serializer.serialize_str(s),
Value::Blob(b) => serializer.serialize_bytes(&b[..]),
Value::List(l) => {
let mut seq = serializer.serialize_seq(Some(l.len()))?;
for elem in l {
seq.serialize_element(&elem)?;
}
seq.end()
}
Value::HashMap(m) => {
let mut map = serializer.serialize_map(Some(m.len()))?;
for (key, value) in m {
map.serialize_entry(&key, &value)?;
}
map.end()
}
Value::OrderedMap(m) => {
let mut map = serializer.serialize_map(Some(m.len()))?;
for (key, value) in m {
map.serialize_entry(&key, &value)?;
}
map.end()
}
Value::HLL(b) => serializer.serialize_bytes(&b[..]),
}
}
}
#[cfg(test)]
mod tests {
use super::Value;
use std::collections::HashMap;
use std::convert::TryInto;
#[test]
fn try_into() {
let _: i64 = Value::Int(42).try_into().unwrap();
let _: f64 = Value::from(42.1).try_into().unwrap();
let _: String = Value::String("hello".into()).try_into().unwrap();
let _: String = Value::GeoJSON(r#"{"type":"Point"}"#.into())
.try_into()
.unwrap();
let _: Vec<u8> = Value::Blob("hello!".into()).try_into().unwrap();
let _: Vec<u8> = Value::HLL("hello!".into()).try_into().unwrap();
let _: bool = Value::Bool(false).try_into().unwrap();
let _: HashMap<Value, Value> = Value::HashMap(HashMap::new()).try_into().unwrap();
let _: Vec<(Value, Value)> = Value::OrderedMap(Vec::new()).try_into().unwrap();
}
#[test]
fn as_string() {
assert_eq!(Value::Nil.as_string(), String::from("<null>"));
assert_eq!(Value::Int(42).as_string(), String::from("42"));
assert_eq!(
Value::UInt(9_223_372_036_854_775_808).as_string(),
String::from("9223372036854775808")
);
assert_eq!(Value::Bool(true).as_string(), String::from("true"));
assert_eq!(Value::from(4.1416).as_string(), String::from("4.1416"));
assert_eq!(
as_geo!(r#"{"type":"Point"}"#).as_string(),
String::from(r#"{"type":"Point"}"#)
);
}
#[test]
fn as_geo() {
let string = String::from(r#"{"type":"Point"}"#);
let str = r#"{"type":"Point"}"#;
assert_eq!(as_geo!(string), as_geo!(str));
}
#[test]
#[cfg(feature = "serialization")]
fn serializer() {
let val: Value = as_list!(
Value::Nil,
"0",
9,
8,
7,
1,
2.1f64,
-1,
as_list!(5, 6, 7, 8, "asd"),
true,
false
);
let json = serde_json::to_string(&val);
assert_eq!(
json.unwrap(),
"[null,\"0\",9,8,7,1,2.1,-1,[5,6,7,8,\"asd\"],true,false]",
"List Serialization failed"
);
let val: Value =
as_map!("a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => as_map!("test"=>123));
let json = serde_json::to_string(&val);
assert_eq!(json.unwrap().len(), 48, "Map Serialization failed");
}
}