use bson::{Bson, Document};
use serde::de::DeserializeOwned;
use super::wire::Message;
use crate::{
bson_util,
error::{CommandError, ErrorKind, Result},
options::StreamAddress,
selection_criteria::ReadPreference,
};
#[derive(Debug, Clone)]
pub(crate) struct Command {
pub(crate) name: String,
pub(crate) target_db: String,
pub(crate) read_pref: Option<ReadPreference>,
pub(crate) body: Document,
}
impl Command {
pub(crate) fn new_read(
name: String,
target_db: String,
read_pref: Option<ReadPreference>,
body: Document,
) -> Self {
Self {
name,
target_db,
read_pref,
body,
}
}
pub(crate) fn new(name: String, target_db: String, body: Document) -> Self {
Self {
name,
target_db,
read_pref: None,
body,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct CommandResponse {
source: StreamAddress,
pub(crate) raw_response: Document,
}
impl CommandResponse {
#[cfg(test)]
pub(crate) fn with_document_and_address(source: StreamAddress, doc: Document) -> Self {
Self {
source,
raw_response: doc,
}
}
#[cfg(test)]
pub(crate) fn with_document(doc: Document) -> Self {
Self::with_document_and_address(
StreamAddress {
hostname: "localhost".to_string(),
port: None,
},
doc,
)
}
pub(crate) fn new(source: StreamAddress, message: Message) -> Result<Self> {
Ok(Self {
source,
raw_response: message.single_document_response()?,
})
}
pub(crate) fn is_success(&self) -> bool {
match self.raw_response.get("ok") {
Some(ref b) => bson_util::get_int(b) == Some(1),
_ => false,
}
}
pub(crate) fn validate(&self) -> Result<()> {
if !self.is_success() {
let command_error: CommandError =
bson::from_bson(Bson::Document(self.raw_response.clone())).map_err(|_| {
ErrorKind::ResponseError {
message: "invalid server response".to_string(),
}
})?;
Err(ErrorKind::CommandError(command_error).into())
} else {
Ok(())
}
}
pub(crate) fn body<T: DeserializeOwned>(&self) -> Result<T> {
match bson::from_bson(Bson::Document(self.raw_response.clone())) {
Ok(body) => Ok(body),
Err(e) => Err(ErrorKind::ResponseError {
message: format!("{}", e),
}
.into()),
}
}
pub(crate) fn source_address(&self) -> &StreamAddress {
&self.source
}
}