Skip to main content

trello/
client.rs

1use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
2use serde::{Deserialize, Serialize};
3use std::error::Error;
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Debug, Deserialize, Serialize)]
8pub struct ClientConfig {
9    #[serde(default = "ClientConfig::default_host")]
10    pub host: String,
11    pub token: String,
12    pub key: String,
13}
14
15#[derive(Debug)]
16pub struct TrelloClient {
17    pub config: ClientConfig,
18    pub client: reqwest::blocking::Client,
19}
20
21impl TrelloClient {
22    pub fn new(config: ClientConfig) -> Self {
23        let auth_value = HeaderValue::from_str(&format!(
24            "OAuth oauth_consumer_key=\"{}\", oauth_token=\"{}\"",
25            config.key, config.token
26        ))
27        .expect("API key and token must contain only visible ASCII characters");
28
29        let mut headers = HeaderMap::new();
30        headers.insert(AUTHORIZATION, auth_value);
31
32        TrelloClient {
33            config,
34            client: reqwest::blocking::Client::builder()
35                .default_headers(headers)
36                .build()
37                .expect("Failed to build HTTP client"),
38        }
39    }
40}
41
42impl ClientConfig {
43    pub fn new(host: &str, token: &str, key: &str) -> Self {
44        ClientConfig {
45            host: String::from(host),
46            token: String::from(token),
47            key: String::from(key),
48        }
49    }
50
51    fn config_dir() -> Result<PathBuf, Box<dyn Error>> {
52        let mut config_path = dirs::config_dir().ok_or("Unable to determine config directory")?;
53        config_path.push("tro");
54
55        Ok(config_path)
56    }
57
58    fn config_path() -> Result<PathBuf, Box<dyn Error>> {
59        let mut config_path = Self::config_dir()?;
60        config_path.push("config.toml");
61
62        Ok(config_path)
63    }
64
65    pub fn save_config(&self) -> Result<(), Box<dyn Error>> {
66        fs::create_dir_all(Self::config_dir()?)?;
67
68        let config_path = Self::config_path()?;
69        debug!("Saving configuration to {:?}", config_path);
70
71        fs::write(config_path, toml::to_string(self)?)?;
72
73        Ok(())
74    }
75
76    pub fn load_config() -> Result<Self, Box<dyn Error>> {
77        let config_path = Self::config_path()?;
78        debug!("Loading configuration from {:?}", config_path);
79        let contents = fs::read_to_string(config_path)?;
80
81        Ok(toml::from_str(&contents)?)
82    }
83
84    pub fn default_host() -> String {
85        String::from("https://api.trello.com")
86    }
87
88    /// Gets the resultant URL of the Trello Config given some path and additional
89    /// parameters. Authentication is handled via the `Authorization` header on the
90    /// HTTP client, not as query parameters.
91    /// ```
92    /// # fn main() -> Result<(), url::ParseError> {
93    /// let config = trello::ClientConfig {
94    ///     host: String::from("https://api.trello.com"),
95    ///     token: String::from("some-token"),
96    ///     key: String::from("some-key"),
97    /// };
98    /// let url = config.get_trello_url("/1/me/boards/", &[])?;
99    /// assert_eq!(
100    ///     url.to_string(),
101    ///     "https://api.trello.com/1/me/boards/"
102    /// );
103    /// let url = config.get_trello_url("/1/boards/some-id/", &[("lists", "open")])?;
104    /// assert_eq!(
105    ///     url.to_string(),
106    ///     "https://api.trello.com/1/boards/some-id/?lists=open",
107    /// );
108    /// # Ok(())
109    /// # }
110    /// ```
111    pub fn get_trello_url(
112        &self,
113        path: &str,
114        params: &[(&str, &str)],
115    ) -> Result<url::Url, url::ParseError> {
116        if params.is_empty() {
117            url::Url::parse(&format!("{}{}", self.host, path))
118        } else {
119            url::Url::parse_with_params(&format!("{}{}", self.host, path), params)
120        }
121    }
122}