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 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}