use std::collections::BTreeMap; use http::header::{HeaderMap, HeaderName}; use http::HeaderValue; use serde::{Deserialize, Serialize}; use crate::{CacheControl, Result, ServerError, Value}; /// Query response #[non_exhaustive] #[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 #[allow(clippy::large_enum_variant)] #[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), } } /// Returns HTTP headers map. pub fn http_headers(&self) -> HeaderMap { match self { BatchResponse::Single(resp) => resp.http_headers.clone(), BatchResponse::Batch(resp) => resp.iter().fold(HeaderMap::new(), |mut acc, resp| { acc.extend(resp.http_headers.clone()); acc }), } } /// Returns HTTP headers iterator. pub fn http_headers_iter(&self) -> impl Iterator { let headers = self.http_headers(); let mut current_name = None; headers.into_iter().filter_map(move |(name, value)| { if let Some(name) = name { current_name = Some(name); } current_name .clone() .map(|current_name| (current_name, value)) }) } } 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"}]"# ); } }