use serde::Deserialize;
use std::fmt::{self, Display, Formatter};
#[derive(Deserialize, Clone, Debug, Default)]
pub struct Response {
pub realtime_start: String,
pub realtime_end: String,
pub observation_start: String,
pub observation_end: String,
pub units: String,
pub output_type: usize,
pub file_type: String,
pub order_by: String,
pub sort_order: String,
pub count: usize,
pub offset: usize,
pub limit: usize,
pub observations: Vec<DataPoint>,
}
impl Display for Response {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for item in self.observations.iter() {
match item.fmt(f) {
Ok(_) => (),
Err(e) => return Err(e),
}
match writeln!(f, "") {
Ok(_) => (),
Err(e) => return Err(e),
}
}
Ok(())
}
}
#[derive(Deserialize, Clone, Debug, Default)]
pub struct DataPoint {
pub realtime_start: String,
pub realtime_end: String,
pub date: String,
pub value: String,
}
impl Display for DataPoint {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "({}: {})", self.date, self.value)
}
}
pub enum SortOrder {
Ascending,
Descending,
}
pub enum Units {
LIN,
CHG,
CH1,
PCH,
PC1,
PCA,
CCH,
CCA,
LOG,
}
pub enum Frequency {
D,
W,
BW,
M,
Q,
SA,
A,
WEF,
WETH,
WEW,
WETU,
WEM,
WESU,
WESA,
BWEW,
BWEM,
}
pub enum AggregationMethod {
AVG,
SUM,
EOP
}
pub enum OutputType {
RT,
VDALL,
VDNEW,
INITIAL
}
pub struct Builder {
option_string: String,
vintage_dates: String,
}
impl Builder {
pub fn new() -> Builder {
Builder {
option_string: String::new(),
vintage_dates: String::new(),
}
}
pub(crate) fn build(mut self) -> String {
if self.vintage_dates.len() > 0 {
self.option_string += format!("&vintage_dates={}", self.vintage_dates).as_str()
}
self.option_string
}
pub fn realtime_start(&mut self, start_date: &str) -> &mut Builder {
self.option_string += format!("&realtime_start={}", start_date).as_str();
self
}
pub fn realtime_end(&mut self, end_date: &str) -> &mut Builder {
self.option_string += format!("&realtime_end={}", end_date).as_str();
self
}
pub fn limit(&mut self, num_points: usize) -> &mut Builder {
let num_points = if num_points > 1000000 { 1000000
} else {
num_points
};
self.option_string += format!("&limit={}", num_points).as_str();
self
}
pub fn offset(&mut self, ofs: usize) -> &mut Builder {
self.option_string += format!("&offset={}", ofs).as_str();
self
}
pub fn sort_order(&mut self, order: SortOrder) -> &mut Builder {
match order {
SortOrder::Descending => {
self.option_string += format!("&sort_order=desc").as_str()
},
_ => () }
self
}
pub fn observation_start(&mut self, start_date: &str) -> &mut Builder {
self.option_string += format!("&observation_start={}", start_date).as_str();
self
}
pub fn observation_end(&mut self, end_date: &str) -> &mut Builder {
self.option_string += format!("&observation_end={}", end_date).as_str();
self
}
pub fn units(&mut self, units: Units) -> &mut Builder {
match units {
Units::CHG => {
self.option_string += format!("&units=chg").as_str()
},
Units::CH1 => {
self.option_string += format!("&units=ch1").as_str()
},
Units::PCH => {
self.option_string += format!("&units=pch").as_str()
},
Units::PC1 => {
self.option_string += format!("&units=pc1").as_str()
},
Units::PCA => {
self.option_string += format!("&units=pca").as_str()
},
Units::CCH => {
self.option_string += format!("&units=cch").as_str()
},
Units::CCA => {
self.option_string += format!("&units=cca").as_str()
},
Units::LOG => {
self.option_string += format!("&units=log").as_str()
},
_ => (), }
self
}
pub fn frequency(&mut self, freq: Frequency) -> &mut Builder {
match freq {
Frequency::D => {
self.option_string += format!("&frequency=d").as_str()
},
Frequency::W => {
self.option_string += format!("&frequency=w").as_str()
},
Frequency::BW => {
self.option_string += format!("&frequency=bw").as_str()
},
Frequency::M => {
self.option_string += format!("&frequency=m").as_str()
},
Frequency::Q => {
self.option_string += format!("&frequency=q").as_str()
},
Frequency::SA => {
self.option_string += format!("&frequency=sa").as_str()
},
Frequency::A => {
self.option_string += format!("&frequency=a").as_str()
},
Frequency::WEF => {
self.option_string += format!("&frequency=wef").as_str()
},
Frequency::WETH => {
self.option_string += format!("&frequency=weth").as_str()
},
Frequency::WEW => {
self.option_string += format!("&frequency=wew").as_str()
},
Frequency::WETU => {
self.option_string += format!("&frequency=d").as_str()
},
Frequency::WEM => {
self.option_string += format!("&frequency=wem").as_str()
},
Frequency::WESU => {
self.option_string += format!("&frequency=wesu").as_str()
},
Frequency::WESA => {
self.option_string += format!("&frequency=wesa").as_str()
},
Frequency::BWEW => {
self.option_string += format!("&frequency=bwew").as_str()
},
Frequency::BWEM => {
self.option_string += format!("&frequency=bwem").as_str()
},
}
self
}
pub fn aggregation_method(&mut self, method: AggregationMethod) -> &mut Builder {
match method {
AggregationMethod::SUM => {
self.option_string += format!("&aggregation_method=sum").as_str()
},
AggregationMethod::EOP => {
self.option_string += format!("&aggregation_method=eop").as_str()
},
_ => () }
self
}
pub fn output_type(&mut self, otype: OutputType) -> &mut Builder {
match otype {
OutputType::VDALL => {
self.option_string += format!("&output_type=2").as_str()
},
OutputType::VDNEW => {
self.option_string += format!("&output_type=3").as_str()
},
OutputType::INITIAL => {
self.option_string += format!("&output_type=4").as_str()
},
_ => () }
self
}
pub fn vintage_date(&mut self, date: &str) -> &mut Builder {
if self.vintage_dates.len() != 0 {
self.vintage_dates.push(',');
}
self.vintage_dates += date;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::FredClient;
#[test]
fn series_observation_with_options() {
let mut c = match FredClient::new() {
Ok(c) => c,
Err(msg) => {
println!("{}", msg);
assert_eq!(2, 1);
return
},
};
let mut builder = Builder::new();
builder
.limit(5)
.sort_order(SortOrder::Descending);
let resp: Response = match c.series_observation("GNPCA", Some(builder)) {
Ok(resp) => resp,
Err(msg) => {
println!("{}", msg);
assert_eq!(2, 1);
return
},
};
for item in resp.observations {
println!("{}: {}", item.date, item.value.parse::<f64>().unwrap());
}
}
}