summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/auth/mod.rs44
-rw-r--r--src/client.rs113
-rw-r--r--src/errors/auth.rs13
3 files changed, 101 insertions, 69 deletions
diff --git a/src/auth/mod.rs b/src/auth/mod.rs
index fae0383..ce550e1 100644
--- a/src/auth/mod.rs
+++ b/src/auth/mod.rs
@@ -11,12 +11,11 @@ use tokio::task::JoinHandle;
use tokio::{select, spawn};
use crate::auth::service::retrieve_auth_code;
-use crate::errors::auth::{AccessTokenRequestError, AuthPromptHandlerError};
+use crate::errors::auth::AuthPromptHandlerError;
mod service;
const AUTH_URL: &str = "https://connect.deezer.com/oauth/auth.php";
-const ACCESS_TOKEN_URL: &str = "https://connect.deezer.com/oauth/access_token.php";
/// A Deezer authentication code.
#[derive(Debug, Deserialize, Clone)]
@@ -96,7 +95,7 @@ impl AuthPromptHandler
}
/// A Deezer access token.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct AccessToken
{
/// The access token.
@@ -105,42 +104,3 @@ pub struct AccessToken
/// The duration until the access token expires.
pub expires: Duration,
}
-
-/// Sends a request for a access token.
-///
-/// # Errors
-/// Will return Err if either sending the request fails or parsing the response fails.
-pub async fn request_access_token(
- app_id: u32,
- secret_key: String,
- auth_code: AuthCode,
-) -> Result<AccessToken, AccessTokenRequestError>
-{
- let response = reqwest::get(format!(
- "{}?app_id={}&secret={}&code={}&output=json",
- ACCESS_TOKEN_URL, app_id, secret_key, auth_code
- ))
- .await?
- .json::<AccessTokenResponse>()
- .await?;
-
- Ok(response.into())
-}
-
-#[derive(Debug, Deserialize)]
-struct AccessTokenResponse
-{
- pub access_token: String,
- pub expires: u64,
-}
-
-impl From<AccessTokenResponse> for AccessToken
-{
- fn from(response: AccessTokenResponse) -> Self
- {
- Self {
- access_token: response.access_token,
- expires: Duration::from_secs(response.expires),
- }
- }
-}
diff --git a/src/client.rs b/src/client.rs
index 0699545..179377f 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,11 +1,12 @@
//! Deezer client.
use std::fmt::Debug;
+use std::time::Duration;
use hyper::client::{Client, HttpConnector};
use hyper::Uri;
use serde::Deserialize;
-use crate::auth::AccessToken;
+use crate::auth::{AccessToken, AuthCode};
use crate::errors::client::DeezerClientError;
use crate::playlist::Playlist;
use crate::user::{User, UserPlaylist, UserPlaylists};
@@ -31,24 +32,45 @@ struct ErrorResponseBody
error: DeezerError,
}
+#[derive(Debug, Deserialize)]
+struct AccessTokenResponse
+{
+ pub access_token: String,
+ pub expires: u64,
+}
+
+impl From<AccessTokenResponse> for AccessToken
+{
+ fn from(response: AccessTokenResponse) -> Self
+ {
+ Self {
+ access_token: response.access_token,
+ expires: Duration::from_secs(response.expires),
+ }
+ }
+}
+
/// Deezer client.
+#[derive(Default)]
pub struct DeezerClient
{
client: Client<HttpConnector>,
- api_url: &'static str,
- access_token: AccessToken,
+ api_uri_authority: &'static str,
+ access_token_uri_authority: &'static str,
+ access_token_uri_path: &'static str,
}
impl DeezerClient
{
/// Creates a new Deezer client.
#[must_use]
- pub fn new(access_token: AccessToken) -> Self
+ pub fn new() -> Self
{
Self {
client: Client::new(),
- api_url: "api.deezer.com",
- access_token,
+ api_uri_authority: "api.deezer.com",
+ access_token_uri_authority: "connect.deezer.com",
+ access_token_uri_path: "/oauth/access_token.php",
}
}
@@ -56,11 +78,17 @@ impl DeezerClient
///
/// # Errors
/// Will return Err if either sending the request or parsing the response fails.
- pub async fn get_me(&self) -> Result<User, DeezerClientError>
+ pub async fn get_me(
+ &self,
+ access_token: AccessToken,
+ ) -> Result<User, DeezerClientError>
{
let response = self
.client
- .get(self.build_endpoint_uri(&"user/me".to_string())?)
+ .get(self.build_endpoint_uri(
+ &"user/me".to_string(),
+ &[("access_token", access_token.access_token)],
+ )?)
.await?;
let body_buf = &*hyper::body::to_bytes(response).await?;
@@ -82,11 +110,15 @@ impl DeezerClient
pub async fn get_user_playlists(
&self,
user_id: u64,
+ access_token: AccessToken,
) -> Result<Vec<UserPlaylist>, DeezerClientError>
{
let response = self
.client
- .get(self.build_endpoint_uri(&format!("user/{}/playlists", user_id))?)
+ .get(self.build_endpoint_uri(
+ &format!("user/{}/playlists", user_id),
+ &[("access_token", access_token.access_token)],
+ )?)
.await?;
let body_buf = &*hyper::body::to_bytes(response).await?;
@@ -111,11 +143,15 @@ impl DeezerClient
pub async fn get_playlist(
&self,
playlist_id: u64,
+ access_token: AccessToken,
) -> Result<Playlist, DeezerClientError>
{
let response = self
.client
- .get(self.build_endpoint_uri(&format!("playlist/{}", playlist_id))?)
+ .get(self.build_endpoint_uri(
+ &format!("playlist/{}", playlist_id),
+ &[("access_token", access_token.access_token)],
+ )?)
.await?;
let body_buf = &*hyper::body::to_bytes(response).await?;
@@ -130,14 +166,63 @@ impl DeezerClient
serde_json::from_slice(body_buf).map_err(DeezerClientError::ParseResponseFailed)
}
- fn build_endpoint_uri(&self, endpoint: &String) -> Result<Uri, DeezerClientError>
+ /// Returns a access token.
+ ///
+ /// # Errors
+ /// Will return Err if either sending the request or parsing the response fails.
+ pub async fn get_access_token(
+ &self,
+ app_id: u32,
+ secret_key: String,
+ auth_code: AuthCode,
+ ) -> Result<AccessToken, DeezerClientError>
+ {
+ let uri = Uri::builder()
+ .scheme("http")
+ .authority(self.access_token_uri_authority)
+ .path_and_query(format!(
+ "{}?app_id={}&secret={}&code={}&output=json",
+ self.access_token_uri_path, app_id, secret_key, auth_code
+ ))
+ .build()
+ .map_err(|_| DeezerClientError::BuildAPIEndpointURIFailed)?;
+
+ let response = self.client.get(uri).await?;
+
+ let body_buf = &*hyper::body::to_bytes(response).await?;
+
+ let err_body_result: Result<ErrorResponseBody, _> =
+ serde_json::from_slice(body_buf);
+
+ if let Ok(err_body) = err_body_result {
+ return Err(DeezerClientError::ReceivedErrorResponse(err_body.error));
+ }
+
+ let access_token_response: AccessTokenResponse = serde_json::from_slice(body_buf)
+ .map_err(DeezerClientError::ParseResponseFailed)?;
+
+ Ok(access_token_response.into())
+ }
+
+ fn build_endpoint_uri(
+ &self,
+ endpoint: &String,
+ query_params: &[(&'static str, String)],
+ ) -> Result<Uri, DeezerClientError>
{
Uri::builder()
.scheme("http")
- .authority(self.api_url)
+ .authority(self.api_uri_authority)
.path_and_query(format!(
- "/{}?access_token={}",
- endpoint, self.access_token.access_token
+ "/{}?{}",
+ endpoint,
+ query_params
+ .iter()
+ .map(|(key, value)| format!("{}={}", key, value))
+ .fold(String::new(), |acc, query_param| format!(
+ "{}&{}",
+ acc, query_param
+ ))
))
.build()
.map_err(|_| DeezerClientError::BuildAPIEndpointURIFailed)
diff --git a/src/errors/auth.rs b/src/errors/auth.rs
index cd4741d..656673e 100644
--- a/src/errors/auth.rs
+++ b/src/errors/auth.rs
@@ -8,16 +8,3 @@ pub enum AuthPromptHandlerError
#[error("HTTP server failed to bind to address")]
BindAddressFailed,
}
-
-/// Access token request error.
-#[derive(Debug, thiserror::Error)]
-pub enum AccessTokenRequestError
-{
- /// Sending access token request failed.
- #[error("Sending access token request failed")]
- SendFailed(#[from] reqwest::Error),
-
- /// Parsing access token respone failed.
- #[error("Parsing access token respone failed")]
- ResponseParseFailed,
-}