use std::io::Cursor;
use std::fs::File;
use serde::de::DeserializeOwned;
use serde_json::Value;
use reqwest::{Body, Client, ClientBuilder, RequestBuilder, Response};
use private;
use super::req::HttpRequest;
use super::res::parsing::{IsOk, Parse};
use super::{build_method, build_url, Error, RequestParams};
pub fn default() -> Result<(Client, RequestParams), Error> {
ClientBuilder::new()
.build()
.map(|cli| (cli, RequestParams::default()))
.map_err(Into::into)
}
pub struct SyncBody(Body);
impl SyncBody {
pub fn into_inner(self) -> Body {
self.0
}
}
impl From<Body> for SyncBody {
fn from(body: Body) -> SyncBody {
SyncBody(body)
}
}
impl From<File> for SyncBody {
fn from(body: File) -> SyncBody {
SyncBody(body.into())
}
}
impl From<Vec<u8>> for SyncBody {
fn from(body: Vec<u8>) -> SyncBody {
SyncBody(body.into())
}
}
impl From<String> for SyncBody {
fn from(body: String) -> SyncBody {
SyncBody(body.into())
}
}
impl From<Value> for SyncBody {
fn from(body: Value) -> SyncBody {
SyncBody(body.to_string().into())
}
}
impl From<&'static [u8]> for SyncBody {
fn from(body: &'static [u8]) -> SyncBody {
SyncBody(Body::new(Cursor::new(body)))
}
}
impl From<&'static str> for SyncBody {
fn from(body: &'static str) -> SyncBody {
SyncBody(Body::new(Cursor::new(body)))
}
}
pub trait SyncElasticClient: private::Sealed {
fn elastic_req<I, B>(&self, params: &RequestParams, req: I) -> Result<Response, Error>
where
I: Into<HttpRequest<'static, B>>,
B: Into<SyncBody>;
}
pub fn build_req<I, B>(client: &Client, params: &RequestParams, req: I) -> RequestBuilder
where
I: Into<HttpRequest<'static, B>>,
B: Into<SyncBody>,
{
let req = req.into();
let url = build_url(&req.url, ¶ms);
let method = build_method(req.method);
let body = req.body;
let mut req = client.request(method, &url);
{
req.headers(params.get_headers());
if let Some(body) = body {
req.body(body.into().into_inner());
}
}
req
}
impl SyncElasticClient for Client {
fn elastic_req<I, B>(&self, params: &RequestParams, req: I) -> Result<Response, Error>
where
I: Into<HttpRequest<'static, B>>,
B: Into<SyncBody>,
{
build_req(&self, params, req).send().map_err(Into::into)
}
}
impl private::Sealed for Client {}
pub trait SyncFromResponse<TResponse> {
fn from_response(self, response: Response) -> Result<TResponse, Error>;
}
impl<TResponse: IsOk + DeserializeOwned> SyncFromResponse<TResponse> for Parse<TResponse> {
fn from_response(self, response: Response) -> Result<TResponse, Error> {
let status: u16 = response.status().into();
self.from_reader(status, response).map_err(Into::into)
}
}
#[cfg(test)]
mod tests {
use reqwest::{Client, Method, RequestBuilder};
use reqwest::header::ContentType;
use super::*;
use req::*;
fn params() -> RequestParams {
RequestParams::new("eshost:9200/path")
.url_param("pretty", true)
.url_param("q", "*")
}
fn expected_req(cli: &Client, method: Method, url: &str, body: Option<Vec<u8>>) -> RequestBuilder {
let mut req = cli.request(method, url);
{
req.header(ContentType::json());
if let Some(body) = body {
req.body(body);
}
}
req
}
fn assert_req(expected: RequestBuilder, actual: RequestBuilder) {
assert_eq!(format!("{:?}", expected), format!("{:?}", actual));
}
#[test]
fn head_req() {
let cli = Client::new();
let req = build_req(&cli, ¶ms(), PingHeadRequest::new());
let url = "eshost:9200/path/?pretty=true&q=*";
let expected = expected_req(&cli, Method::Head, url, None);
assert_req(expected, req);
}
#[test]
fn get_req() {
let cli = Client::new();
let req = build_req(&cli, ¶ms(), SimpleSearchRequest::new());
let url = "eshost:9200/path/_search?pretty=true&q=*";
let expected = expected_req(&cli, Method::Get, url, None);
assert_req(expected, req);
}
#[test]
fn post_req() {
let cli = Client::new();
let req = build_req(
&cli,
¶ms(),
PercolateRequest::for_index_ty("idx", "ty", vec![]),
);
let url = "eshost:9200/path/idx/ty/_percolate?pretty=true&q=*";
let expected = expected_req(&cli, Method::Post, url, Some(vec![]));
assert_req(expected, req);
}
#[test]
fn put_req() {
let cli = Client::new();
let req = build_req(
&cli,
¶ms(),
IndicesCreateRequest::for_index("idx", vec![]),
);
let url = "eshost:9200/path/idx?pretty=true&q=*";
let expected = expected_req(&cli, Method::Put, url, Some(vec![]));
assert_req(expected, req);
}
#[test]
fn delete_req() {
let cli = Client::new();
let req = build_req(&cli, ¶ms(), IndicesDeleteRequest::for_index("idx"));
let url = "eshost:9200/path/idx?pretty=true&q=*";
let expected = expected_req(&cli, Method::Delete, url, None);
assert_req(expected, req);
}
#[test]
fn file_into_body() {
SyncBody::from(File::open("Cargo.toml").unwrap());
}
#[test]
fn owned_string_into_body() {
SyncBody::from(String::new());
}
#[test]
fn borrowed_string_into_body() {
SyncBody::from("abc");
}
#[test]
fn owned_vec_into_body() {
SyncBody::from(Vec::new());
}
#[test]
fn borrowed_vec_into_body() {
static BODY: &'static [u8] = &[0, 1, 2];
SyncBody::from(BODY);
}
#[test]
fn empty_body_into_body() {
SyncBody::from(empty_body());
}
#[test]
fn json_value_into_body() {
SyncBody::from(json!({}));
}
}