Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace HashMap<String, String> with HashMap<&str, &str> to reduce allocation #200

Merged
merged 11 commits into from
Apr 19, 2021
298 changes: 164 additions & 134 deletions src/client.rs

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions src/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub use self::reqwest::ReqwestClient as HTTPClient;
pub use self::ureq::UreqClient as HTTPClient;

pub type Headers = HashMap<String, String>;
pub type Query = HashMap<String, String>;
pub type Form = HashMap<String, String>;
pub type Query<'a> = HashMap<&'a str, &'a str>;
pub type Form<'a> = HashMap<&'a str, &'a str>;

pub mod headers {
use crate::oauth2::Token;
Expand Down Expand Up @@ -86,11 +86,11 @@ pub trait BaseHttpClient: Default + Clone + fmt::Debug {
payload: &Value,
) -> ClientResult<String>;

async fn post_form(
async fn post_form<'a>(
&self,
url: &str,
headers: Option<&Headers>,
payload: &Form,
payload: &Form<'a>,
) -> ClientResult<String>;

async fn put(
Expand Down Expand Up @@ -148,7 +148,7 @@ impl Spotify {
&self,
url: &str,
headers: Option<&Headers>,
payload: &Query,
payload: &Query<'_>,
) -> ClientResult<String> {
let url = self.endpoint_url(url);
self.http.get(&url, headers, payload).await
Expand All @@ -172,7 +172,7 @@ impl Spotify {
&self,
url: &str,
headers: Option<&Headers>,
payload: &Form,
payload: &Form<'_>,
) -> ClientResult<String> {
let url = self.endpoint_url(url);
self.http.post_form(&url, headers, payload).await
Expand Down Expand Up @@ -206,7 +206,11 @@ impl Spotify {
/// autentication.
#[inline]
#[maybe_async]
pub(crate) async fn endpoint_get(&self, url: &str, payload: &Query) -> ClientResult<String> {
pub(crate) async fn endpoint_get(
&self,
url: &str,
payload: &Query<'_>,
) -> ClientResult<String> {
let headers = self.auth_headers()?;
self.get(url, Some(&headers), payload).await
}
Expand Down
4 changes: 2 additions & 2 deletions src/http/reqwest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@ impl BaseHttpClient for ReqwestClient {
}

#[inline]
async fn post_form(
async fn post_form<'a>(
&self,
url: &str,
headers: Option<&Headers>,
payload: &Form,
payload: &Form<'a>,
) -> ClientResult<String> {
self.request(Method::POST, url, headers, |req| req.form(payload))
.await
Expand Down
6 changes: 3 additions & 3 deletions src/http/ureq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ impl BaseHttpClient for UreqClient {
}

#[inline]
fn post_form(
fn post_form<'a>(
&self,
url: &str,
headers: Option<&Headers>,
payload: &Form,
payload: &Form<'a>,
) -> ClientResult<String> {
let request = ureq::post(url);
let sender = |req: Request| {
let payload = payload
.iter()
.map(|(key, val)| (key.as_str(), val.as_str()))
.map(|(key, val)| (*key, *val))
.collect::<Vec<_>>();

req.send_form(&payload)
Expand Down
4 changes: 2 additions & 2 deletions src/model/enums/country.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use serde::{Deserialize, Serialize};
use strum::ToString;
use strum::AsRefStr;

/// ISO 3166-1 alpha-2 country code, from
/// [country-list](https://datahub.io/core/country-list)
///
/// [Reference](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
pub enum Country {
#[strum(serialize = "AF")]
#[serde(rename = "AF")]
Expand Down
27 changes: 15 additions & 12 deletions src/model/enums/misc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use serde::{Deserialize, Serialize};
use strum::ToString;
use strum::AsRefStr;

use super::Country;

Expand All @@ -8,7 +8,7 @@ use super::Country;
/// `toggling_shuffle`, `toggling_repeat_track`, `transferring_playback`.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#disallows-object)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, Hash, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, Hash, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DisallowKey {
Expand All @@ -27,7 +27,7 @@ pub enum DisallowKey {
/// Time range: `long-term`, `medium-term`, `short-term`.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/personalization/get-users-top-artists-and-tracks/)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum TimeRange {
Expand All @@ -39,7 +39,7 @@ pub enum TimeRange {
/// Repeat state: `track`, `context` or `off`.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/player/set-repeat-mode-on-users-playback/)
#[derive(Clone, Debug, Copy, Serialize, Deserialize, PartialEq, Eq, ToString)]
#[derive(Clone, Debug, Copy, Serialize, Deserialize, PartialEq, Eq, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RepeatState {
Expand All @@ -51,7 +51,7 @@ pub enum RepeatState {
/// Type for include_external: `audio`.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/search/search/)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum IncludeExternal {
Expand All @@ -61,7 +61,7 @@ pub enum IncludeExternal {
/// Date precision: `year`, `month`, `day`.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/):
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum DatePrecision {
Expand All @@ -73,7 +73,7 @@ pub enum DatePrecision {
/// The reason for the restriction: `market`, `product`, `explicit`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#track-restriction-object)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum RestrictionReason {
Expand All @@ -87,7 +87,7 @@ pub enum RestrictionReason {
/// a -1 for `no result`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-analysis/#section-object)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
pub enum Modality {
Minor = 0,
Major = 1,
Expand All @@ -102,12 +102,15 @@ pub enum Market {
Country(Country),
FromToken,
}
pub trait AsRefStr {
fn as_ref(&self) -> &str;
}

impl ToString for Market {
fn to_string(&self) -> String {
impl AsRefStr for Market {
fn as_ref(&self) -> &str {
match self {
Market::Country(c) => c.to_string(),
Market::FromToken => "from_token".to_string(),
Market::Country(country) => country.as_ref(),
Market::FromToken => "from_token",
}
}
}
22 changes: 12 additions & 10 deletions src/model/enums/types.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString, ToString};
use strum::{AsRefStr, Display, EnumString};

/// Copyright type: `C` = the copyright, `P` = the sound recording (performance)
/// copyright.
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#copyright-object)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
pub enum CopyrightType {
#[strum(serialize = "P")]
#[serde(rename = "P")]
Expand All @@ -18,7 +18,7 @@ pub enum CopyrightType {
/// Album type: `album`, `single`, `appears_on`, `compilation`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#album-object-full)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AlbumType {
Expand All @@ -29,7 +29,9 @@ pub enum AlbumType {
}

/// Type: `artist`, `album`, `track`, `playlist`, `show` or `episode`
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, Display, EnumString)]
#[derive(
Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, Display, EnumString, AsRefStr,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum Type {
Expand All @@ -45,7 +47,7 @@ pub enum Type {
/// Additional typs: `track`, `episode`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-information-about-the-users-current-playback/)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AdditionalType {
Expand All @@ -56,7 +58,7 @@ pub enum AdditionalType {
/// Currently playing type: `track`, `episode`, `ad`, `unknown`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-the-users-currently-playing-track/)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum CurrentlyPlayingType {
Expand All @@ -71,7 +73,7 @@ pub enum CurrentlyPlayingType {
/// Type for search: `artist`, `album`, `track`, `playlist`, `show`, `episode`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/#category-search)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum SearchType {
Expand All @@ -88,7 +90,7 @@ pub enum SearchType {
/// (The subscription level "open" can be considered the same as "free".)
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/users-profile/get-current-users-profile/)
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, ToString)]
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Eq, Debug, AsRefStr)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum SubscriptionLevel {
Expand All @@ -100,7 +102,7 @@ pub enum SubscriptionLevel {
/// Device Type: `computer`, `smartphone`, `speaker`, `TV`
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/player/get-a-users-available-devices/#device-types)
#[derive(Clone, Debug, Serialize, Deserialize, ToString, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, AsRefStr)]
#[strum(serialize_all = "snake_case")]
pub enum DeviceType {
Computer,
Expand All @@ -121,7 +123,7 @@ pub enum DeviceType {
/// Recommendations seed type
///
/// [Reference](https://developer.spotify.com/documentation/web-api/reference/object-model/#recommendations-seed-object)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, AsRefStr)]
#[serde(rename_all = "snake_case")]
pub enum RecommendationsSeedType {
Artist,
Expand Down
41 changes: 15 additions & 26 deletions src/oauth2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ impl Spotify {

/// Sends a request to Spotify for an access token.
#[maybe_async]
async fn fetch_access_token(&self, payload: &Form) -> ClientResult<Token> {
async fn fetch_access_token(&self, payload: &Form<'_>) -> ClientResult<Token> {
// This request uses a specific content type, and the client ID/secret
// as the authentication, since the access token isn't available yet.
let mut head = Headers::new();
Expand All @@ -272,11 +272,8 @@ impl Spotify {
refresh_token: &str,
) -> ClientResult<()> {
let mut data = Form::new();
data.insert(headers::REFRESH_TOKEN.to_owned(), refresh_token.to_owned());
data.insert(
headers::GRANT_TYPE.to_owned(),
headers::GRANT_REFRESH_TOKEN.to_owned(),
);
data.insert(headers::REFRESH_TOKEN, refresh_token);
data.insert(headers::GRANT_TYPE, headers::GRANT_REFRESH_TOKEN);

let mut tok = self.fetch_access_token(&data).await?;
tok.refresh_token = Some(refresh_token.to_string());
Expand All @@ -299,10 +296,7 @@ impl Spotify {
#[maybe_async]
pub async fn request_client_token_without_cache(&mut self) -> ClientResult<()> {
let mut data = Form::new();
data.insert(
headers::GRANT_TYPE.to_owned(),
headers::GRANT_CLIENT_CREDS.to_owned(),
);
data.insert(headers::GRANT_TYPE, headers::GRANT_CLIENT_CREDS);

self.token = Some(self.fetch_access_token(&data).await?);

Expand Down Expand Up @@ -338,22 +332,17 @@ impl Spotify {
pub async fn request_user_token_without_cache(&mut self, code: &str) -> ClientResult<()> {
let oauth = self.get_oauth()?;
let mut data = Form::new();
data.insert(
headers::GRANT_TYPE.to_owned(),
headers::GRANT_AUTH_CODE.to_owned(),
);
data.insert(headers::REDIRECT_URI.to_owned(), oauth.redirect_uri.clone());
data.insert(headers::CODE.to_owned(), code.to_owned());
data.insert(
headers::SCOPE.to_owned(),
oauth
.scope
.clone()
.into_iter()
.collect::<Vec<_>>()
.join(" "),
);
data.insert(headers::STATE.to_owned(), oauth.state.clone());
let scopes = oauth
.scope
.clone()
.into_iter()
.collect::<Vec<_>>()
.join(" ");
data.insert(headers::GRANT_TYPE, headers::GRANT_AUTH_CODE);
data.insert(headers::REDIRECT_URI, oauth.redirect_uri.as_ref());
data.insert(headers::CODE, code);
data.insert(headers::SCOPE, scopes.as_ref());
data.insert(headers::STATE, oauth.state.as_ref());

self.token = Some(self.fetch_access_token(&data).await?);

Expand Down
Loading