use std::collections::HashMap;
#[cfg(feature = "serde")]
use serde::de::IntoDeserializer;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::qdrant::{GeoPoint, Struct, Value};
#[cfg(feature = "serde")]
use crate::QdrantError;
#[derive(Clone, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
pub struct Payload(pub(crate) HashMap<String, Value>);
impl Payload {
pub fn new() -> Self {
Self(HashMap::new())
}
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
pub fn insert(&mut self, key: impl ToString, val: impl Into<Value>) {
self.0.insert(key.to_string(), val.into());
}
#[cfg(feature = "serde")]
pub fn deserialize<T: serde::de::DeserializeOwned>(self) -> Result<T, QdrantError> {
Ok(T::deserialize(
Struct { fields: self.0 }.into_deserializer(),
)?)
}
}
impl From<HashMap<String, Value>> for Payload {
#[inline]
fn from(payload: HashMap<String, Value>) -> Self {
Self(payload)
}
}
impl From<HashMap<&str, Value>> for Payload {
#[inline]
fn from(payload: HashMap<&str, Value>) -> Self {
Self(
payload
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.collect(),
)
}
}
impl From<Payload> for HashMap<String, Value> {
#[inline]
fn from(payload: Payload) -> Self {
payload.0
}
}
#[cfg(feature = "serde")]
impl From<Payload> for serde_json::Value {
#[inline]
fn from(value: Payload) -> serde_json::Value {
serde_json::Value::Object(value.into())
}
}
#[cfg(feature = "serde")]
impl From<Payload> for serde_json::Map<String, serde_json::Value> {
#[inline]
fn from(value: Payload) -> serde_json::Map<String, serde_json::Value> {
value
.0
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<serde_json::Map<String, serde_json::Value>>()
}
}
#[cfg(feature = "serde")]
impl TryFrom<serde_json::Value> for Payload {
type Error = crate::QdrantError;
#[inline]
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
if let serde_json::Value::Object(object) = value {
Ok(object.into())
} else {
Err(crate::QdrantError::JsonToPayload(value))
}
}
}
#[cfg(feature = "serde")]
impl From<serde_json::Map<String, serde_json::Value>> for Payload {
#[inline]
fn from(object: serde_json::Map<String, serde_json::Value>) -> Self {
Payload::from(
object
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<HashMap<String, Value>>(),
)
}
}
#[cfg(feature = "serde")]
impl From<HashMap<String, serde_json::Value>> for Payload {
fn from(value: HashMap<String, serde_json::Value>) -> Self {
Payload::from(
value
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect::<HashMap<String, Value>>(),
)
}
}
impl<K, const N: usize> From<[(K, Value); N]> for Payload
where
K: Into<String>,
{
fn from(values: [(K, Value); N]) -> Self {
let mut map = HashMap::with_capacity(N);
for (k, v) in values {
map.insert(k.into(), v);
}
Self(map)
}
}
impl From<GeoPoint> for Value {
fn from(point: GeoPoint) -> Self {
use crate::qdrant::value::Kind;
let map = HashMap::from([
("lat".to_string(), point.lat.into()),
("lon".to_string(), point.lon.into()),
]);
Self {
kind: Some(Kind::StructValue(Struct { fields: map })),
}
}
}
#[cfg(feature = "serde")]
#[cfg(test)]
mod tests {
use serde_json::json;
use super::{Payload, *};
#[test]
fn json_payload_round_trip() {
let payload: Payload = vec![
("some_string", "Bar".into()),
("some_bool", true.into()),
("some_int", 12.into()),
("some_float", 2.3.into()),
("some_seq", vec!["elem1", "elem2"].into()),
("some_obj", vec![("key", "value")].into()),
]
.into_iter()
.collect::<HashMap<_, Value>>()
.into();
let json_value = serde_json::to_string(&payload).unwrap();
let payload_back: Payload = serde_json::from_str(&json_value).unwrap();
assert_eq!(payload, payload_back);
}
#[test]
fn payload_from_string() {
let json = r#"{
"some_string": "Bar",
"some_bool": true,
"some_int": 12,
"some_float": 2.3,
"some_seq": ["elem1", "elem2"],
"some_obj": {"key": "value"}
}"#;
let parsed_payload: Payload = serde_json::from_str(json).unwrap();
let expected: Payload = vec![
("some_string", "Bar".into()),
("some_bool", true.into()),
("some_int", 12.into()),
("some_float", 2.3.into()),
("some_seq", vec!["elem1", "elem2"].into()),
("some_obj", vec![("key", "value")].into()),
]
.into_iter()
.collect::<HashMap<_, Value>>()
.into();
assert_eq!(parsed_payload, expected);
}
#[test]
fn test_json_macro() {
let json_value = json!({
"some_string": "Bar",
"some_bool": true,
"some_int": 12,
"some_float": 2.3,
"some_seq": ["elem1", "elem2"],
"some_obj": {"key": "value"}
});
let payload: Payload = Payload::try_from(json_value).unwrap();
eprintln!("payload = {payload:#?}");
}
}