use std::time::Duration;
use bson::{bson, doc};
use serde::{Serialize, Serializer};
use serde_with::skip_serializing_none;
use typed_builder::TypedBuilder;
use crate::{
bson_util,
error::{ErrorKind, Result},
};
#[derive(Clone, Debug)]
pub enum ReadConcern {
Local,
Majority,
Linearizable,
Available,
Custom(String),
}
impl PartialEq for ReadConcern {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl ReadConcern {
pub fn as_str(&self) -> &str {
match *self {
ReadConcern::Local => "local",
ReadConcern::Majority => "majority",
ReadConcern::Linearizable => "linearizable",
ReadConcern::Available => "available",
ReadConcern::Custom(ref s) => s,
}
}
}
impl Serialize for ReadConcern {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
(doc! {
"level": self.as_str().to_string()
})
.serialize(serializer)
}
}
#[skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, TypedBuilder, Serialize)]
pub struct WriteConcern {
#[builder(default)]
pub w: Option<Acknowledgment>,
#[builder(default)]
#[serde(rename = "wtimeout")]
#[serde(serialize_with = "bson_util::serialize_duration_as_i64_millis")]
pub w_timeout: Option<Duration>,
#[builder(default)]
#[serde(rename = "j")]
pub journal: Option<bool>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Acknowledgment {
Nodes(i32),
Majority,
Tag(String),
}
impl Serialize for Acknowledgment {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Acknowledgment::Majority => serializer.serialize_str("majority"),
Acknowledgment::Nodes(n) => serializer.serialize_i32(n.clone()),
Acknowledgment::Tag(tag) => serializer.serialize_str(tag),
}
}
}
impl From<i32> for Acknowledgment {
fn from(i: i32) -> Self {
Acknowledgment::Nodes(i)
}
}
impl From<String> for Acknowledgment {
fn from(s: String) -> Self {
if s == "majority" {
Acknowledgment::Majority
} else {
Acknowledgment::Tag(s)
}
}
}
#[cfg(test)]
use bson::Bson;
impl Acknowledgment {
#[cfg(test)]
pub(crate) fn to_bson(&self) -> Bson {
match self {
Acknowledgment::Nodes(i) => Bson::I64(i64::from(*i)),
Acknowledgment::Majority => Bson::String("majority".to_string()),
Acknowledgment::Tag(s) => Bson::String(s.to_string()),
}
}
}
impl WriteConcern {
pub fn validate(&self) -> Result<()> {
if let Some(Acknowledgment::Nodes(i)) = self.w {
if i < 0 {
return Err(ErrorKind::ArgumentError {
message: "write concern `w` field cannot be negative integer".to_string(),
}
.into());
}
}
if self.w == Some(Acknowledgment::Nodes(0)) && self.journal == Some(true) {
return Err(ErrorKind::ArgumentError {
message: "write concern cannot have w=0 and j=true".to_string(),
}
.into());
}
if let Some(w_timeout) = self.w_timeout {
if w_timeout < Duration::from_millis(0) {
return Err(ErrorKind::ArgumentError {
message: "write concern `w_timeout` field cannot be negative".to_string(),
}
.into());
}
}
Ok(())
}
}