1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use std;
use futures::{Future, Stream};
use hyper::{self, Client as HyperClient, Method, Request, Uri};
use serde::{Deserialize, Serialize};
use tokio_core::reactor::Core;
use super::error::{Result, ResultExt};
use super::xmlfmt::{Call, Fault, Params, Response, parse, from_params, into_params};

pub struct Client {
    core: Core,
    client: HyperClient<hyper::client::HttpConnector>,
}

impl Client {
    pub fn new() -> Result<Client> {
        let core = Core::new().chain_err(|| "Failed to initialize Tokio Core.")?;
        let client = HyperClient::new(&core.handle());
        Ok(Client {
            core: core,
            client: client,
        })
    }

    pub fn call_value<Tkey>(&mut self, uri: &Uri, name: Tkey, params: Params) -> Result<Response>
    where
        Tkey: Into<String>,
    {
        use super::xmlfmt::value::ToXml;
        let body = Call {
            name: name.into(),
            params,
        }.to_xml();
        let mut request = Request::new(Method::Post, uri.clone());
        request.headers_mut().set(hyper::header::ContentLength(
            body.len() as u64,
        ));
        request.headers_mut().set(hyper::header::ContentType::xml());
        request.set_body(body);
        let work = self.client.request(request).and_then(|res| {
            res.body().concat2().map(|chunk| chunk.to_vec())
        });
        let response = self.core.run(work).chain_err(
            || "Failed to run the HTTP request within Tokio Core.",
        )?;
        parse::response(response.as_slice()).map_err(Into::into)
    }

    pub fn call<'a, Tkey, Treq, Tres>(
        &mut self,
        uri: &Uri,
        name: Tkey,
        req: Treq,
    ) -> Result<std::result::Result<Tres, Fault>>
    where
        Tkey: Into<String>,
        Treq: Serialize,
        Tres: Deserialize<'a>,
    {
        match self.call_value(uri, name, into_params(&req)?) {
            Ok(Ok(v)) => from_params(v).map(Ok).map_err(Into::into),
            Ok(Err(v)) => Ok(Err(v)),
            Err(v) => Err(v),
        }
    }
}