pub use crate::endpoints::directory::earnings_transcript_list;
use crate::{
Client, Result,
endpoints::{
EndpointSpec, QueryEncoder, QueryParameters,
metadata::{EndpointBounds, EndpointMetadata, GeographicAvailability},
},
query::{Quarter, Year},
responses::transcripts::{
EarningsTranscript, EarningsTranscriptDate, LatestEarningsTranscript,
},
types::{Limit, Page, Ticker},
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct LatestEarningsTranscriptsQuery {
limit: Option<Limit>,
page: Option<Page>,
}
impl LatestEarningsTranscriptsQuery {
pub const fn new() -> Self {
Self {
limit: None,
page: None,
}
}
pub const fn with_limit(mut self, limit: Limit) -> Self {
self.limit = Some(limit);
self
}
pub const fn with_page(mut self, page: Page) -> Self {
self.page = Some(page);
self
}
pub const fn limit(&self) -> Option<Limit> {
self.limit
}
pub const fn page(&self) -> Option<Page> {
self.page
}
}
impl QueryParameters for LatestEarningsTranscriptsQuery {
fn encode(&self, encoder: &mut QueryEncoder<'_>) {
encoder.optional("limit", self.limit);
encoder.optional("page", self.page);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EarningsTranscriptQuery {
symbol: Ticker,
year: Year,
quarter: Quarter,
limit: Option<Limit>,
}
impl EarningsTranscriptQuery {
pub fn new(symbol: Ticker, year: Year, quarter: Quarter) -> Self {
Self {
symbol,
year,
quarter,
limit: None,
}
}
pub const fn with_limit(mut self, limit: Limit) -> Self {
self.limit = Some(limit);
self
}
pub const fn symbol(&self) -> &Ticker {
&self.symbol
}
pub const fn year(&self) -> Year {
self.year
}
pub const fn quarter(&self) -> Quarter {
self.quarter
}
pub const fn limit(&self) -> Option<Limit> {
self.limit
}
}
impl QueryParameters for EarningsTranscriptQuery {
fn encode(&self, encoder: &mut QueryEncoder<'_>) {
encoder.required("symbol", &self.symbol);
encoder.required("year", self.year);
encoder.required("quarter", self.quarter);
encoder.optional("limit", self.limit);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EarningsTranscriptDatesQuery {
symbol: Ticker,
}
impl EarningsTranscriptDatesQuery {
pub const fn new(symbol: Ticker) -> Self {
Self { symbol }
}
pub const fn symbol(&self) -> &Ticker {
&self.symbol
}
}
impl From<Ticker> for EarningsTranscriptDatesQuery {
fn from(symbol: Ticker) -> Self {
Self::new(symbol)
}
}
impl From<&Ticker> for EarningsTranscriptDatesQuery {
fn from(symbol: &Ticker) -> Self {
Self::new(symbol.clone())
}
}
impl QueryParameters for EarningsTranscriptDatesQuery {
fn encode(&self, encoder: &mut QueryEncoder<'_>) {
encoder.required("symbol", &self.symbol);
}
}
const LATEST_TRANSCRIPTS_METADATA: EndpointMetadata = EndpointMetadata::new()
.with_geography(GeographicAvailability::Worldwide)
.with_bounds(EndpointBounds::new().with_response_rows(100).with_page(100));
const WORLDWIDE: EndpointMetadata =
EndpointMetadata::new().with_geography(GeographicAvailability::Worldwide);
pub fn latest_earnings_transcripts(
query: LatestEarningsTranscriptsQuery,
) -> EndpointSpec<LatestEarningsTranscriptsQuery, Vec<LatestEarningsTranscript>> {
EndpointSpec::get(
"earning-call-transcript-latest",
"earning-call-transcript-latest",
query,
)
.with_metadata(LATEST_TRANSCRIPTS_METADATA)
}
pub fn earnings_transcript(
query: EarningsTranscriptQuery,
) -> EndpointSpec<EarningsTranscriptQuery, Vec<EarningsTranscript>> {
EndpointSpec::get("earning-call-transcript", "earning-call-transcript", query)
.with_metadata(WORLDWIDE)
}
pub fn earnings_transcript_dates(
query: EarningsTranscriptDatesQuery,
) -> EndpointSpec<EarningsTranscriptDatesQuery, Vec<EarningsTranscriptDate>> {
EndpointSpec::get(
"earning-call-transcript-dates",
"earning-call-transcript-dates",
query,
)
.with_metadata(WORLDWIDE)
}
impl Client {
pub async fn latest_earnings_transcripts(
&self,
query: impl Into<LatestEarningsTranscriptsQuery>,
) -> Result<Vec<LatestEarningsTranscript>> {
self.execute(&latest_earnings_transcripts(query.into()))
.await
}
pub async fn earnings_transcript(
&self,
query: impl Into<EarningsTranscriptQuery>,
) -> Result<Vec<EarningsTranscript>> {
self.execute(&earnings_transcript(query.into())).await
}
pub async fn earnings_transcript_dates(
&self,
query: impl Into<EarningsTranscriptDatesQuery>,
) -> Result<Vec<EarningsTranscriptDate>> {
self.execute(&earnings_transcript_dates(query.into())).await
}
}
#[cfg(test)]
mod tests {
use super::*;
fn pairs(query: &impl QueryParameters) -> Vec<(String, String)> {
let mut pairs = Vec::new();
query.encode(&mut QueryEncoder::new(&mut |name, value| {
pairs.push((name.to_owned(), value.to_owned()));
}));
pairs
}
#[test]
fn latest_query_omits_absent_values_and_encodes_limit_then_page() {
assert!(pairs(&LatestEarningsTranscriptsQuery::new()).is_empty());
let query = LatestEarningsTranscriptsQuery::new()
.with_limit(Limit(100))
.with_page(Page(0));
assert_eq!(
pairs(&query),
[
("limit".to_owned(), "100".to_owned()),
("page".to_owned(), "0".to_owned()),
]
);
}
#[test]
fn transcript_query_encodes_symbol_year_quarter_then_limit() {
let query =
EarningsTranscriptQuery::new(Ticker::new("AAPL").unwrap(), Year(2020), Quarter::Q3)
.with_limit(Limit(1));
assert_eq!(
pairs(&query),
[
("symbol".to_owned(), "AAPL".to_owned()),
("year".to_owned(), "2020".to_owned()),
("quarter".to_owned(), "3".to_owned()),
("limit".to_owned(), "1".to_owned()),
]
);
}
#[test]
fn transcript_dates_query_encodes_only_the_required_symbol() {
let query = EarningsTranscriptDatesQuery::new(Ticker::new("AAPL").unwrap());
assert_eq!(pairs(&query), [("symbol".to_owned(), "AAPL".to_owned())]);
}
}