use std::convert::{TryFrom, TryInto};
use serde::de::{Error as _, Unexpected};
use crate::{extjson::models, oid, Bson, Document};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
InvalidObjectId(oid::Error),
DeserializationError { message: String },
}
impl std::fmt::Display for Error {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Self::InvalidObjectId(ref err) => err.fmt(fmt),
Self::DeserializationError { ref message } => message.fmt(fmt),
}
}
}
impl std::error::Error for Error {}
impl serde::de::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Self::DeserializationError {
message: format!("{}", msg),
}
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::DeserializationError {
message: err.to_string(),
}
}
}
impl From<oid::Error> for Error {
fn from(err: oid::Error) -> Self {
Self::InvalidObjectId(err)
}
}
pub type Result<T> = std::result::Result<T, Error>;
impl TryFrom<serde_json::Map<String, serde_json::Value>> for Bson {
type Error = Error;
fn try_from(obj: serde_json::Map<String, serde_json::Value>) -> Result<Self> {
if obj.contains_key("$oid") {
let oid: models::ObjectId = serde_json::from_value(obj.into())?;
return Ok(Bson::ObjectId(oid.parse()?));
}
if obj.contains_key("$symbol") {
let symbol: models::Symbol = serde_json::from_value(obj.into())?;
return Ok(Bson::Symbol(symbol.value));
}
if obj.contains_key("$regularExpression") {
let regex: models::Regex = serde_json::from_value(obj.into())?;
return Ok(regex.parse().into());
}
if obj.contains_key("$numberInt") {
let int: models::Int32 = serde_json::from_value(obj.into())?;
return Ok(Bson::Int32(int.parse()?));
}
if obj.contains_key("$numberLong") {
let int: models::Int64 = serde_json::from_value(obj.into())?;
return Ok(Bson::Int64(int.parse()?));
}
if obj.contains_key("$numberDouble") {
let double: models::Double = serde_json::from_value(obj.into())?;
return Ok(Bson::Double(double.parse()?));
}
if obj.contains_key("$numberDecimal") {
let decimal: models::Decimal128 = serde_json::from_value(obj.into())?;
return Ok(Bson::Decimal128(decimal.parse()?));
}
if obj.contains_key("$binary") {
let binary: models::Binary = serde_json::from_value(obj.into())?;
return Ok(Bson::Binary(binary.parse()?));
}
if obj.contains_key("$uuid") {
let uuid: models::Uuid = serde_json::from_value(obj.into())?;
return Ok(Bson::Binary(uuid.parse()?));
}
if obj.contains_key("$code") {
let code_w_scope: models::JavaScriptCodeWithScope = serde_json::from_value(obj.into())?;
return match code_w_scope.scope {
Some(scope) => Ok(crate::JavaScriptCodeWithScope {
code: code_w_scope.code,
scope: scope.try_into()?,
}
.into()),
None => Ok(Bson::JavaScriptCode(code_w_scope.code)),
};
}
if obj.contains_key("$timestamp") {
let ts: models::Timestamp = serde_json::from_value(obj.into())?;
return Ok(ts.parse().into());
}
if obj.contains_key("$date") {
let extjson_datetime: models::DateTime = serde_json::from_value(obj.into())?;
return Ok(Bson::DateTime(extjson_datetime.parse()?));
}
if obj.contains_key("$minKey") {
let min_key: models::MinKey = serde_json::from_value(obj.into())?;
return min_key.parse();
}
if obj.contains_key("$maxKey") {
let max_key: models::MaxKey = serde_json::from_value(obj.into())?;
return max_key.parse();
}
if obj.contains_key("$dbPointer") {
let db_ptr: models::DbPointer = serde_json::from_value(obj.into())?;
return Ok(db_ptr.parse()?.into());
}
if obj.contains_key("$undefined") {
let undefined: models::Undefined = serde_json::from_value(obj.into())?;
return undefined.parse();
}
Ok(Bson::Document(obj.try_into()?))
}
}
impl TryFrom<serde_json::Value> for Bson {
type Error = Error;
fn try_from(value: serde_json::Value) -> Result<Self> {
match value {
serde_json::Value::Number(x) => x
.as_i64()
.map(|i| {
if i >= std::i32::MIN as i64 && i <= std::i32::MAX as i64 {
Bson::Int32(i as i32)
} else {
Bson::Int64(i)
}
})
.or_else(|| x.as_f64().map(Bson::from))
.ok_or_else(|| {
Error::invalid_value(
Unexpected::Other(format!("{}", x).as_str()),
&"a number that could fit in i32, i64, or f64",
)
}),
serde_json::Value::String(x) => Ok(x.into()),
serde_json::Value::Bool(x) => Ok(x.into()),
serde_json::Value::Array(x) => Ok(Bson::Array(
x.into_iter()
.map(Bson::try_from)
.collect::<Result<Vec<Bson>>>()?,
)),
serde_json::Value::Null => Ok(Bson::Null),
serde_json::Value::Object(map) => map.try_into(),
}
}
}
impl TryFrom<serde_json::Map<String, serde_json::Value>> for Document {
type Error = Error;
fn try_from(obj: serde_json::Map<String, serde_json::Value>) -> Result<Self> {
Ok(obj
.into_iter()
.map(|(k, v)| -> Result<(String, Bson)> {
let value: Bson = v.try_into()?;
Ok((k, value))
})
.collect::<Result<Vec<(String, Bson)>>>()?
.into_iter()
.collect())
}
}