use std::collections::HashMap;
use aws_sdk_dynamodb::types::AttributeValue;
use serde_json::Value as Json;
use crate::errors::GraphDDBError;
use crate::pyfloat::py_repr;
use crate::value::indexmap_shim::IndexMap;
use crate::value::Value;
pub fn serialize_string(s: impl Into<String>) -> AttributeValue {
AttributeValue::S(s.into())
}
pub fn serialize_json(value: &Json) -> Result<AttributeValue, GraphDDBError> {
Ok(match value {
Json::String(s) => AttributeValue::S(s.clone()),
Json::Bool(b) => AttributeValue::Bool(*b),
Json::Null => AttributeValue::Null(true),
Json::Number(n) => AttributeValue::N(number_to_ddb_string(n)?),
Json::Array(items) => {
let mut out = Vec::with_capacity(items.len());
for it in items {
out.push(serialize_json(it)?);
}
AttributeValue::L(out)
}
Json::Object(map) => {
let mut out: HashMap<String, AttributeValue> = HashMap::new();
for (k, v) in map {
out.insert(k.clone(), serialize_json(v)?);
}
AttributeValue::M(out)
}
})
}
pub fn serialize_int(n: i64) -> AttributeValue {
AttributeValue::N(n.to_string())
}
fn number_to_ddb_string(n: &serde_json::Number) -> Result<String, GraphDDBError> {
if let Some(i) = n.as_i64() {
Ok(i.to_string())
} else if let Some(u) = n.as_u64() {
Ok(u.to_string())
} else if let Some(f) = n.as_f64() {
py_repr(f)
} else {
Err(GraphDDBError::new(format!("cannot serialize number {n}")))
}
}
pub fn deserialize(av: &AttributeValue) -> Value {
match av {
AttributeValue::S(s) => Value::S(s.clone()),
AttributeValue::N(n) => Value::N(n.clone()),
AttributeValue::Bool(b) => Value::Bool(*b),
AttributeValue::Null(_) => Value::Null,
AttributeValue::L(items) => Value::L(items.iter().map(deserialize).collect()),
AttributeValue::M(map) => {
let mut out = IndexMap::new();
for (k, v) in map {
out.insert(k.clone(), deserialize(v));
}
Value::M(out)
}
AttributeValue::Ss(items) => Value::L(items.iter().map(|s| Value::S(s.clone())).collect()),
AttributeValue::Ns(items) => Value::L(items.iter().map(|n| Value::N(n.clone())).collect()),
_ => Value::Null,
}
}
pub fn deserialize_item(item: &HashMap<String, AttributeValue>) -> IndexMap<Value> {
let mut keys: Vec<&String> = item.keys().collect();
keys.sort();
let mut out = IndexMap::new();
for k in keys {
out.insert(k.clone(), deserialize(&item[k]));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn serialize_int_and_float_ddb_strings() {
let n = serialize_json(&json!(1)).unwrap();
assert!(matches!(n, AttributeValue::N(ref s) if s == "1"));
let neg = serialize_json(&json!(-1)).unwrap();
assert!(matches!(neg, AttributeValue::N(ref s) if s == "-1"));
let f = serialize_json(&json!(1.5)).unwrap();
assert!(matches!(f, AttributeValue::N(ref s) if s == "1.5"));
}
#[test]
fn deserialize_keeps_number_string() {
let av = AttributeValue::N("12345678901234567890".to_string());
assert_eq!(
deserialize(&av),
Value::N("12345678901234567890".to_string())
);
}
}