use crate::error::Error; use chrono::serde::ts_seconds; use chrono::DateTime; use chrono::Utc; use reqwest; use serde::Deserialize; use serde::Serialize; use serde_json::from_value; use serde_json::Value; pub trait StravaApi { fn get(&self, method: &str, access_token: &str, parasm: &T) -> Result; } pub struct StravaImpl { client: reqwest::blocking::Client, base_url: String, } impl StravaImpl { pub fn new() -> StravaImpl { StravaImpl { client: reqwest::blocking::Client::new(), base_url: "https://www.strava.com/api/v3".to_string(), } } } impl StravaApi for StravaImpl { fn get(&self, method: &str, access_token: &str, params: &T) -> Result { let uri = format!("{}{}", self.base_url, method); let response = self.client.get(&uri) .bearer_auth(access_token) .query(params) .send()?; info!("StravaApi::get({}) returned {:?}", method, response); let json = response.json()?; Ok(json) } } #[derive(Serialize, Deserialize, Debug)] pub struct AthleteSummary { id: i64, username: String, firstname: String, lastname: String, } #[derive(Serialize, Deserialize, Debug)] pub struct Token { #[serde(with = "ts_seconds")] pub expires_at: DateTime, pub refresh_token: String, pub access_token: String, pub athlete: AthleteSummary, } pub fn exchange_token(client_id: &str, client_secret: &str, code: &str) -> Result { let client = reqwest::blocking::Client::new(); let params = [ ("client_id", client_id), ("client_secret", client_secret), ("code", code), ]; let uri = "https://www.strava.com/oauth/token"; let req = client.post(uri).form(¶ms); let json: Value = req.send().map(|r| r.json())??; from_value(json).map_err(From::from) }