use crate::accounts::Account;
use crate::messages::Messages;
use base64::{engine::general_purpose, Engine as _};
use std::collections::HashMap;
use url::Url;
pub struct Nylas {
pub client_id: String,
pub client_secret: String,
pub account: Option<Account>,
pub access_token: Option<String>,
pub messages: Option<Messages<'static>>,
}
impl Nylas {
pub async fn new(
client_id: &str,
client_secret: &str,
access_token: Option<&str>,
) -> Result<Self, String> {
let mut nylas = Nylas {
client_id: client_id.to_string(),
client_secret: client_secret.to_string(),
access_token: access_token.map(|s| s.to_string()),
account: None,
messages: None,
};
if let Some(_) = nylas.access_token {
if let Err(error) = nylas.account().await {
return Err(format!("Error initializing Nylas: {}", error));
}
}
Ok(nylas)
}
pub fn authentication_url(
&self,
redirect_uri: &str,
login_hint: Option<&str>,
state: Option<&str>,
scopes: Option<&str>,
) -> Result<String, String> {
if self.client_id.is_empty() || self.client_secret.is_empty() {
return Err("Client ID and Client Secret must not be empty.".to_string());
}
if !Url::parse(redirect_uri).is_ok() {
return Err("Invalid redirect URI.".to_string());
}
let mut params: HashMap<&str, String> = HashMap::new();
params.insert("client_id", self.client_id.clone());
params.insert("redirect_uri", redirect_uri.to_string());
params.insert("response_type", "code".to_string());
if let Some(login_hint) = login_hint {
params.insert("login_hint", login_hint.to_string());
}
if let Some(state) = state {
params.insert("state", state.to_string());
}
if let Some(scopes) = scopes {
params.insert("scopes", scopes.to_string());
}
let base_url = "https://api.nylas.com/oauth/authorize";
let mut url = String::from(base_url);
url.push('?');
for (key, value) in params.iter() {
url.push_str(key);
url.push_str("=");
url.push_str(value);
url.push('&');
}
url.pop();
Ok(url)
}
pub async fn exchange_access_token(&self, authorization_code: &str) -> Result<String, String> {
if self.client_id.is_empty() || self.client_secret.is_empty() {
return Err("Client ID and Client Secret must not be empty.".to_string());
}
let mut params: HashMap<&str, String> = HashMap::new();
params.insert("client_id", self.client_id.clone());
params.insert("client_secret", self.client_secret.clone());
params.insert("grant_type", "authorization_code".to_string());
params.insert("code", authorization_code.to_string());
let base_url = "https://api.nylas.com/oauth/token";
let encoded_client_secret = general_purpose::STANDARD.encode(self.client_secret.clone());
let client = reqwest::Client::new();
let response = client
.post(base_url)
.header("Authorization", format!("Basic {}", encoded_client_secret))
.header("Accept", "application/json")
.form(¶ms)
.send()
.await
.map_err(|e| format!("Request Error: {:?}", e))?;
if response.status().is_success() {
let data: HashMap<String, String> = response
.json()
.await
.map_err(|e| format!("JSON Parsing Error: {:?}", e))?;
if let Some(access_token) = data.get("access_token") {
return Ok(access_token.to_string());
} else {
return Err("Access token not found in the response.".to_string());
}
} else {
let error_message = response
.text()
.await
.unwrap_or_else(|_| "Unknown Error".to_string());
return Err(format!("HTTP Error: - {}", error_message));
}
}
pub async fn account(&mut self) -> Result<(), String> {
if self.client_id.is_empty() || self.client_secret.is_empty() {
return Err("Client ID and Client Secret must not be empty.".to_string());
}
if let Some(access_token) = &self.access_token {
let base_url = "https://api.nylas.com/account";
let client = reqwest::Client::new();
let response = client
.get(base_url)
.header("Authorization", format!("Bearer {}", access_token))
.header("Accept", "application/json")
.send()
.await
.map_err(|e| format!("Request Error: {:?}", e))?;
if response.status().is_success() {
let account: Account = response
.json()
.await
.map_err(|e| format!("JSON Parsing Error: {:?}", e))?;
self.account = Some(account);
Ok(())
} else {
Err(format!("HTTP Error: {}", response.status()))
}
} else {
Err("Access token must be set before calling the account method.".to_string())
}
}
pub fn messages(&mut self) -> Messages {
Messages { nylas: self }
}
}