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

Implement timezones endpoint #20

Merged
merged 1 commit into from
Oct 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "marketstack"
version = "0.0.6"
version = "0.0.7"
edition = "2021"
license = "MIT"
description = "Rust bindings for Marketstack REST API"
Expand Down
1 change: 1 addition & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub mod dividends;
pub mod eod;
pub mod paged;
pub mod splits;
pub mod timezones;

pub use self::client::AsyncClient;
pub use self::client::Client;
Expand Down
112 changes: 112 additions & 0 deletions src/api/timezones.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//! Implementation of the `timezones` API endpoint.
//!
//! This endpoint is used to lookup the timezones supported by Marketstack.

use derive_builder::Builder;

use crate::api::paged::PaginationError;
use crate::api::{endpoint_prelude::*, ApiError};

/// Query for `timezones`.
#[derive(Debug, Clone, Builder)]
#[builder(setter(strip_option))]
pub struct Timezones {
/// Pagination limit for API request.
#[builder(setter(name = "_limit"), default)]
limit: Option<PageLimit>,
/// Pagination offset value for API request.
#[builder(default)]
offset: Option<u64>,
}

impl Timezones {
/// Create a builder for this endpoint.
pub fn builder() -> TimezonesBuilder {
TimezonesBuilder::default()
}
}

impl TimezonesBuilder {
/// Limit the number of results returned.
pub fn limit(&mut self, limit: u16) -> Result<&mut Self, ApiError<PaginationError>> {
let new = self;
new.limit = Some(Some(PageLimit::new(limit)?));
Ok(new)
}
}

impl Endpoint for Timezones {
fn method(&self) -> Method {
Method::GET
}

fn endpoint(&self) -> Cow<'static, str> {
"timezones".into()
}

fn parameters(&self) -> QueryParams {
let mut params = QueryParams::default();

params
.push_opt("limit", self.limit.clone())
.push_opt("offset", self.offset);

params
}
}

#[cfg(test)]
mod tests {

use crate::api::timezones::Timezones;
use crate::api::{self, Query};
use crate::test::client::{ExpectedUrl, SingleTestClient};

#[test]
fn timezones_defaults_are_sufficient() {
Timezones::builder().build().unwrap();
}

#[test]
fn timezones_endpoint() {
let endpoint = ExpectedUrl::builder()
.endpoint("timezones")
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");

let endpoint = Timezones::builder().build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}

#[test]
fn timezones_limit() {
let endpoint = ExpectedUrl::builder()
.endpoint("timezones")
.add_query_params(&[("limit", "50")])
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");

let endpoint = Timezones::builder().limit(50).unwrap().build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}

#[test]
fn timezones_over_limit() {
assert!(Timezones::builder().limit(9999).is_err());
}

#[test]
fn timezones_offset() {
let endpoint = ExpectedUrl::builder()
.endpoint("timezones")
.add_query_params(&[("offset", "2")])
.build()
.unwrap();
let client = SingleTestClient::new_raw(endpoint, "");

let endpoint = Timezones::builder().offset(2).build().unwrap();
api::ignore(endpoint).query(&client).unwrap();
}
}
56 changes: 55 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,31 @@ pub struct CurrenciesData {
pub data: Vec<CurrenciesDataItem>,
}

/// Rust representation of single data item from Marketstack `timezones` response.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimezonesDataItem {
/// Name of the given timezone.
pub timezone: String,
/// Abbreviation of the given timezone.
pub abbr: String,
/// Summer time abbreviation of the given timezone.
pub abbr_dst: String,
}

/// Rust representation of the JSON response from `timezones` marketstack endpoint.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TimezonesData {
/// Corresponds to pagination entry from JSON response from marketstack.
pub pagination: PaginationInfo,
/// Corresponds to data entry from JSON response from marketstack.
pub data: Vec<TimezonesDataItem>,
}

#[cfg(test)]
mod tests {
use chrono::NaiveDate;

use crate::{CurrenciesData, DividendsData, EodData, SplitsData};
use crate::{CurrenciesData, DividendsData, EodData, SplitsData, TimezonesData};

#[test]
fn test_deserialize_eod() {
Expand Down Expand Up @@ -298,4 +318,38 @@ mod tests {
assert_eq!(currencies_data.data[0].symbol, "$");
assert_eq!(currencies_data.data[0].name, "US Dollar");
}

#[test]
fn test_deserialize_timezones() {
let json_data = r#"{
"pagination": {
"limit": 3,
"offset": 0,
"count": 3,
"total": 57
},
"data": [
{
"timezone": "America/New_York",
"abbr": "EST",
"abbr_dst": "EDT"
},
{
"timezone": "America/Argentina/Buenos_Aires",
"abbr": "-03",
"abbr_dst": "-03"
},
{
"timezone": "Europe/Vienna",
"abbr": "CET",
"abbr_dst": "CEST"
}
]
}"#;

let timezones_data: TimezonesData = serde_json::from_str(json_data).unwrap();
assert_eq!(timezones_data.data[0].timezone, "America/New_York");
assert_eq!(timezones_data.data[0].abbr, "EST");
assert_eq!(timezones_data.data[0].abbr_dst, "EDT");
}
}
44 changes: 44 additions & 0 deletions tests/timezones.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use marketstack::api::{timezones, AsyncQuery, Query};
use marketstack::{AsyncMarketstack, Marketstack, TimezonesData};

mod setup;

#[test]
#[ignore]
fn test_timezones() {
let api_key = setup::setup_key();
let client = Marketstack::new_insecure("api.marketstack.com", api_key).unwrap();

let endpoint = timezones::Timezones::builder()
.limit(3)
.unwrap()
.build()
.unwrap();
let timezones_result: TimezonesData = endpoint.query(&client).unwrap();

assert_eq!(timezones_result.pagination.limit, 3);
assert_eq!(timezones_result.pagination.offset, 0);

assert_eq!(timezones_result.data.len(), 3);
}

#[tokio::test]
#[ignore]
async fn test_async_timezones() {
let api_key = setup::setup_key();
let client = AsyncMarketstack::new_insecure("api.marketstack.com", api_key)
.await
.unwrap();

let endpoint = timezones::Timezones::builder()
.limit(3)
.unwrap()
.build()
.unwrap();
let timezones_result: TimezonesData = endpoint.query_async(&client).await.unwrap();

assert_eq!(timezones_result.pagination.limit, 3);
assert_eq!(timezones_result.pagination.offset, 0);

assert_eq!(timezones_result.data.len(), 3);
}