use std::collections::BTreeMap; use http::header::HeaderMap; use serde::{Deserialize, Serialize}; use crate::{CacheControl, Result, ServerError, Value}; /// Query response #[derive(Debug, Default, Serialize, Deserialize, PartialEq)] pub struct Response { /// Data of query result #[serde(default)] pub data: Value, /// Extensions result #[serde(skip_serializing_if = "BTreeMap::is_empty", default)] pub extensions: BTreeMap, /// Cache control value #[serde(skip)] pub cache_control: CacheControl, /// Errors #[serde(skip_serializing_if = "Vec::is_empty", default)] pub errors: Vec, /// HTTP headers #[serde(skip)] pub http_headers: HeaderMap, } impl Response { /// Create a new successful response with the data. #[must_use] pub fn new(data: impl Into) -> Self { Self { data: data.into(), ..Default::default() } } /// Create a response from some errors. #[must_use] pub fn from_errors(errors: Vec) -> Self { Self { errors, ..Default::default() } } /// Set the extension result of the response. #[must_use] pub fn extension(mut self, name: impl Into, value: Value) -> Self { self.extensions.insert(name.into(), value); self } /// Set the http headers of the response. #[must_use] pub fn http_headers(self, http_headers: HeaderMap) -> Self { Self { http_headers, ..self } } /// Set the cache control of the response. #[must_use] pub fn cache_control(self, cache_control: CacheControl) -> Self { Self { cache_control, ..self } } /// Returns `true` if the response is ok. #[inline] pub fn is_ok(&self) -> bool { self.errors.is_empty() } /// Returns `true` if the response is error. #[inline] pub fn is_err(&self) -> bool { !self.is_ok() } /// Extract the error from the response. Only if the `error` field is empty will this return /// `Ok`. #[inline] pub fn into_result(self) -> Result> { if self.is_err() { Err(self.errors) } else { Ok(self) } } } /// Response for batchable queries #[derive(Debug, Serialize)] #[serde(untagged)] pub enum BatchResponse { /// Response for single queries Single(Response), /// Response for batch queries Batch(Vec), } impl BatchResponse { /// Gets cache control value pub fn cache_control(&self) -> CacheControl { match self { BatchResponse::Single(resp) => resp.cache_control, BatchResponse::Batch(resp) => resp.iter().fold(CacheControl::default(), |acc, item| { acc.merge(&item.cache_control) }), } } /// Returns `true` if all responses are ok. pub fn is_ok(&self) -> bool { match self { BatchResponse::Single(resp) => resp.is_ok(), BatchResponse::Batch(resp) => resp.iter().all(Response::is_ok), } } /// Provides an iterator over all of the HTTP headers set on the response pub fn http_headers(&self) -> impl Iterator { let it: Box> = match self { BatchResponse::Single(resp) => Box::new( resp.http_headers .iter() .map(|(key, value)| (key.as_str(), value.as_str())), ), BatchResponse::Batch(resp) => Box::new( resp.iter() .map(|r| { r.http_headers .iter() .map(|(key, value)| (key.as_str(), value.as_str())) }) .flatten(), ), }; it } } impl From for BatchResponse { fn from(response: Response) -> Self { Self::Single(response) } } impl From> for BatchResponse { fn from(responses: Vec) -> Self { Self::Batch(responses) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_batch_response_single() { let resp = BatchResponse::Single(Response::new(Value::Boolean(true))); assert_eq!(serde_json::to_string(&resp).unwrap(), r#"{"data":true}"#); } #[test] fn test_batch_response_batch() { let resp = BatchResponse::Batch(vec![ Response::new(Value::Boolean(true)), Response::new(Value::String("1".to_string())), ]); assert_eq!( serde_json::to_string(&resp).unwrap(), r#"[{"data":true},{"data":"1"}]"# ); } }