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
//! Works with the API

use crate::{
    error::{APIError, Result},
    API_VERSION,
};
use reqwest::{Client, Response};
use serde_json::{from_value, Map, Value};
use std::collections::HashMap;

/// A HashMap which contains method parameters
pub type Params = HashMap<String, String>;

/// An API client used to call API methods.
#[derive(Debug)]
pub struct APIClient {
    client: Client,
    token: String,
}

impl APIClient {
    /// Creates a new `APIClient`, given an access token.
    ///
    /// # Panics
    /// This method panics if native TLS backend cannot be created or initialized by the `reqwest` crate.
    ///
    /// See [reqwest docs](https://docs.rs/reqwest/0.9.*/reqwest/struct.Client.html#panic) for more information.
    pub fn new(token: String) -> APIClient {
        APIClient {
            client: Client::new(),
            token,
        }
    }

    /// Calls an API method, given its name and parameters.
    pub fn call_method(&self, method_name: &str, mut params: Params) -> Result<Value> {
        params.insert("v".into(), API_VERSION.into());
        params.insert("access_token".into(), self.token.clone());

        let response_result: Result<Response> = self
            .client
            .get(&("https://api.vk.com/method/".to_owned() + method_name))
            .query(&params)
            .send()
            .map_err(|e| e.into());
        let mut response = response_result?;

        let value_result: Result<Value> = response.json().map_err(|e| e.into());
        let value = value_result?;

        let api_response_result: Result<&Map<String, Value>> = value
            .as_object()
            .ok_or_else(|| "API response is not an object!".into());
        let api_response = api_response_result?;

        match api_response.get("response") {
            Some(ok) => Ok(ok.clone()),
            None => match api_response.get("error") {
                Some(err) => Err(from_value::<APIError>(err.clone())?.into()),
                None => Err("The API responded with neither a response nor an error!".into()),
            },
        }
    }
}