use std::fmt;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
use bson::{Bson, oid::ObjectId};
use serde::{Serialize, Serializer};
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum MongoId {
ObjectId(ObjectId),
String(String),
}
impl Serialize for MongoId {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl MongoId {
pub fn from_bson(value: &Bson) -> Option<Self> {
match value {
Bson::ObjectId(oid) => Some(MongoId::ObjectId(*oid)),
Bson::String(s) => Some(MongoId::String(s.clone())),
_ => None,
}
}
pub fn to_bson(&self) -> Bson {
match self {
MongoId::ObjectId(oid) => Bson::ObjectId(*oid),
MongoId::String(s) => Bson::String(s.clone()),
}
}
}
pub(crate) fn id_variants(value: &Bson) -> Option<Bson> {
match value {
Bson::ObjectId(oid) => Some(Bson::String(oid.to_hex())),
Bson::String(s) if s.len() == 24 => ObjectId::parse_str(s).ok().map(Bson::ObjectId),
_ => None,
}
}
pub(crate) fn eq_filter(path: &str, value: &Bson) -> bson::Document {
match id_variants(value) {
Some(alt) => bson::doc! { path: { "$in": [value.clone(), alt] } },
None => bson::doc! { path: value.clone() },
}
}
impl Hash for MongoId {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
MongoId::ObjectId(oid) => oid.hash(state),
MongoId::String(s) => s.hash(state),
}
}
}
impl fmt::Display for MongoId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MongoId::ObjectId(oid) => write!(f, "{}", oid),
MongoId::String(s) => write!(f, "{}", s),
}
}
}
impl FromStr for MongoId {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(oid) = ObjectId::parse_str(s) {
Ok(MongoId::ObjectId(oid))
} else {
Ok(MongoId::String(s.to_string()))
}
}
}
impl From<MongoId> for Bson {
fn from(id: MongoId) -> Self {
id.to_bson()
}
}
impl From<ObjectId> for MongoId {
fn from(oid: ObjectId) -> Self {
MongoId::ObjectId(oid)
}
}
impl From<String> for MongoId {
fn from(s: String) -> Self {
if s.len() == 24
&& let Ok(oid) = ObjectId::parse_str(&s)
{
return MongoId::ObjectId(oid);
}
MongoId::String(s)
}
}
impl From<&str> for MongoId {
fn from(s: &str) -> Self {
if s.len() == 24
&& let Ok(oid) = ObjectId::parse_str(s)
{
return MongoId::ObjectId(oid);
}
MongoId::String(s.to_string())
}
}