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
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
extern crate hyper;
extern crate hyper_tls;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate futures;

use futures::Future;
use futures::Stream;

pub struct Translate {
    api_key: String,
    client: hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>
}

impl Translate {
    pub fn new(api_key: &str) -> Translate {
        Translate {
            api_key: api_key.to_owned(),
            client: hyper::Client::builder()
                    .build(hyper_tls::HttpsConnector::new(4).expect("Failed to initialize HttpsConnector"))
        }
    }

    pub fn translate(&self, text: &str, from_lang: &str, to_lang: &str) -> Box<Future<Item=String,Error=Error> + Send> {
        let uri = "https://translate.yandex.net/api/v1.5/tr.json/translate";

        let body = TranslateRequestBody {
            text: text.to_owned(),
            key: self.api_key.to_owned(),
            lang: format!("{}-{}", from_lang, to_lang)
        };

        let body = match serde_urlencoded::to_string(&body) {
            Ok(x) => x,
            Err(err) => return Box::new(futures::future::err(Error::Other(
                        format!("Failed to serialize request body: {:?}", err))))
        };

        let req = match hyper::Request::post(uri)
            .header(hyper::header::CONTENT_TYPE, "application/x-www-form-urlencoded")
            .header(hyper::header::CONTENT_LENGTH, body.len() as u64)
            .body(body.into()) {
                Ok(x) => x,
                Err(err) => return Box::new(futures::future::err(Error::Other(format!("Failed to construct request: {:?}", err))))
            };

        Box::new(self.client.request(req)
                 .map_err(|e| Error::Other(format!("Failed to send request: {:?}", e)))
                 .and_then(|res| {
                     res.into_body().concat2().map_err(|e| Error::Other(format!("Failed to get response: {:?}", e)))
                 })
                 .and_then(|res| {
                     let result = serde_json::from_slice(&res);
                     match result {
                         Ok(TranslateResponse::Error(err_res)) => Err(match err_res.code {
                             _ => Error::Other(format!("Unable to translate: {}", err_res.message))
                         }),
                         Ok(TranslateResponse::Success(resp)) => resp.text.into_iter().next().ok_or_else(|| Error::Other("No strings returned.".to_owned())),
                         Err(e) => Err(Error::Other(format!("Unable to parse response: {:?}", e)))
                     }
                 }))
    }
}

pub enum Error {
    Other(String)
}

impl std::fmt::Debug for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        std::fmt::Display::fmt(self, fmt)
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(fmt, "{}", std::error::Error::description(self))
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Other(ref x) => &x
        }
    }
}

#[derive(Serialize)]
struct TranslateRequestBody {
    text: String,
    key: String,
    lang: String
}

#[derive(Deserialize)]
struct TranslateErrorResponse {
    code: u16,
    message: String
}

#[derive(Deserialize)]
struct TranslateSuccessResponse {
    text: Vec<String>
}

#[derive(Deserialize)]
#[serde(untagged)]
enum TranslateResponse {
    Error(TranslateErrorResponse),
    Success(TranslateSuccessResponse)
}