use crate::{
BeaErr, Data, Dataset, DatasetMissing, Datasets, FixedAssetData, GdpData, IipData,
InputOutputData, ItaData, KeyMissing, Method, MneDiData, NipaData, NotObject, Notes,
ParameterValues, Parameters, ParseInt, RequestParameters, map_to_string,
};
#[derive(
Clone, Debug, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize, derive_more::From,
)]
pub enum Results {
#[from(ApiError)]
ApiError(ApiError),
#[from(Data)]
Data(Data),
#[from(Datasets)]
Datasets(Datasets),
#[from(MneError)]
MneError(MneError),
#[from(Parameters)]
Parameters(Parameters),
#[from(ParameterValues)]
ParameterValues(ParameterValues),
#[from(RequestsExceeded)]
RequestsExceeded(RequestsExceeded),
}
impl Results {
#[tracing::instrument(skip_all)]
pub fn into_data(&self) -> Option<Data> {
match self {
Self::Data(d) => Some(d.clone()),
_ => None,
}
}
#[tracing::instrument(skip_all)]
pub fn into_datasets(&self) -> Option<Datasets> {
match self {
Self::Datasets(d) => Some(d.clone()),
_ => None,
}
}
#[tracing::instrument(skip_all)]
pub fn into_parameters(&self) -> Option<Parameters> {
match self {
Self::Parameters(p) => Some(p.clone()),
_ => None,
}
}
#[tracing::instrument(skip_all)]
pub fn into_parameter_values(&self) -> Option<ParameterValues> {
match self {
Self::ParameterValues(p) => Some(p.clone()),
_ => None,
}
}
#[tracing::instrument(skip_all)]
pub fn read_json(
value: &serde_json::Value,
request: &RequestParameters,
) -> Result<Self, BeaErr> {
let method = request.method()?;
match method {
Method::GetData => {
tracing::trace!("Trying Data...");
let dataset = request.dataset()?;
match dataset {
Dataset::Nipa | Dataset::NIUnderlyingDetail => {
if let Ok(t) = NipaData::try_from(value) {
let data = Data::from(t);
return Ok(Self::from(data));
}
}
Dataset::FixedAssets => {
if let Ok(t) = FixedAssetData::try_from(value) {
let data = Data::from(t);
return Ok(Self::from(data));
}
}
Dataset::Mne => match MneDiData::try_from(value) {
Ok(t) => {
let data = Data::from(t);
return Ok(Self::from(data));
}
Err(source) => {
tracing::debug!("{source}");
}
},
Dataset::GDPbyIndustry | Dataset::UnderlyingGDPbyIndustry => {
match GdpData::try_from(value) {
Ok(t) => {
let data = Data::from(t);
return Ok(Self::from(data));
}
Err(source) => {
tracing::trace!("{source}");
}
}
}
Dataset::Ita => match ItaData::try_from(value) {
Ok(t) => {
let data = Data::from(t);
return Ok(Self::from(data));
}
Err(source) => {
tracing::trace!("{source}");
}
},
Dataset::Iip => match IipData::try_from(value) {
Ok(t) => {
let data = Data::from(t);
return Ok(Self::from(data));
}
Err(source) => {
tracing::trace!("{source}");
}
},
Dataset::InputOutput => match InputOutputData::try_from(value) {
Ok(t) => {
let data = Data::from(t);
return Ok(Self::from(data));
}
Err(source) => {
tracing::trace!("{source}");
}
},
_ => {
let error =
DatasetMissing::new(dataset.to_string(), line!(), file!().to_string());
return Err(error.into());
}
}
}
Method::GetDataSetList => {
tracing::trace!("Trying datasets...");
if let Ok(t) = Datasets::try_from(value.clone()) {
return Ok(Self::from(t));
}
}
Method::GetParameterList => {
tracing::trace!("Trying parameters...");
match Parameters::try_from(value) {
Ok(t) => {
tracing::trace!("Parameters found, returning...");
return Ok(Self::from(t));
}
Err(source) => {
tracing::trace!("{source}");
}
}
}
Method::GetParameterValues | Method::GetParameterValuesFiltered => {
tracing::trace!("Trying parameter values...");
if let Ok(t) = ParameterValues::try_from(value) {
return Ok(Self::from(t));
}
}
}
tracing::trace!("Trying ApiError...");
if let Ok(t) = ApiError::try_from(value) {
tracing::info!("ApiError found.");
return Ok(Self::from(t));
}
tracing::trace!("Trying MneError...");
if let Ok(t) = MneError::try_from(value) {
tracing::info!("MneError found.");
return Ok(Self::from(t));
}
tracing::trace!("Trying RequestsExceeded...");
if let Ok(t) = RequestsExceeded::try_from(value) {
tracing::info!("RequestsExceeded found.");
return Ok(Self::from(t));
}
let error = KeyMissing::new(
"Not results table.".to_string(),
line!(),
file!().to_string(),
);
Err(error.into())
}
}
#[derive(
Clone,
Debug,
PartialEq,
PartialOrd,
serde::Deserialize,
serde::Serialize,
derive_new::new,
derive_getters::Getters,
)]
#[serde(rename_all = "PascalCase")]
pub struct Beaapi {
request: RequestParameters,
results: Results,
notes: Option<Notes>,
}
impl Beaapi {
#[tracing::instrument(skip_all)]
pub fn read_json(m: &serde_json::Map<String, serde_json::Value>) -> Result<Self, BeaErr> {
let key = "Request".to_string();
let request = if let Some(value) = m.get(&key) {
RequestParameters::try_from(value)?
} else {
let error = KeyMissing::new(key, line!(), file!().to_string());
return Err(error.into());
};
let key = "Results".to_string();
let results;
let notes;
if let Some(value) = m.get(&key) {
results = Results::read_json(value, &request)?;
notes = Notes::try_from(value).ok();
} else {
let error = KeyMissing::new(key, line!(), file!().to_string());
return Err(error.into());
};
let beaapi = Self::new(request, results, notes);
Ok(beaapi)
}
}
impl TryFrom<&serde_json::Value> for Beaapi {
type Error = BeaErr;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
tracing::trace!("Reading Beaapi.");
match value {
serde_json::Value::Object(m) => {
let bea = Self::read_json(m)?;
Ok(bea)
}
_ => {
tracing::warn!("Invalid Value: {value:#?}");
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
}
}
#[derive(
Clone,
Debug,
PartialEq,
PartialOrd,
serde::Deserialize,
serde::Serialize,
derive_more::From,
derive_more::Deref,
derive_more::DerefMut,
)]
#[from(Beaapi)]
pub struct BeaResponse {
#[serde(rename = "BEAAPI")]
beaapi: Beaapi,
}
impl BeaResponse {
#[tracing::instrument(skip_all)]
pub fn data(&self) -> Option<Data> {
self.beaapi.results.into_data()
}
#[tracing::instrument(skip_all)]
pub fn datasets(&self) -> Option<Datasets> {
self.beaapi.results.into_datasets()
}
#[tracing::instrument(skip_all)]
pub fn parameters(&self) -> Option<Parameters> {
self.beaapi.results.into_parameters()
}
#[tracing::instrument(skip_all)]
pub fn parameter_values(&self) -> Option<ParameterValues> {
self.beaapi.results.into_parameter_values()
}
pub fn into_parts(&self) -> (RequestParameters, Results) {
let req = self.request().clone();
let res = self.results().clone();
(req, res)
}
}
impl TryFrom<&serde_json::Value> for BeaResponse {
type Error = BeaErr;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
tracing::trace!("Reading BeaResponse.");
match value {
serde_json::Value::Object(m) => {
let key = "BEAAPI".to_string();
if let Some(val) = m.get(&key) {
let beaapi = Beaapi::try_from(val)?;
Ok(Self { beaapi })
} else {
tracing::trace!("Invalid Object: {m:#?}");
let error = KeyMissing::new(key, line!(), file!().to_string());
Err(error.into())
}
}
_ => {
tracing::trace!("Invalid Value: {value:#?}");
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
}
}
#[derive(
Clone,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Deserialize,
serde::Serialize,
derive_new::new,
derive_more::Display,
)]
#[display("API Error - Code: {} - Description: {}", self.code, self.description)]
pub struct ApiError {
code: i32,
description: String,
}
impl ApiError {
pub fn read_json(mp: &serde_json::Map<String, serde_json::Value>) -> Result<Self, BeaErr> {
let key = "Error".to_string();
if let Some(value) = mp.get(&key) {
match value {
serde_json::Value::Object(m) => {
let description = map_to_string("APIErrorDescription", m)?;
let code = map_to_string("APIErrorCode", m)?;
match str::parse::<i32>(&code) {
Ok(code) => Ok(Self::new(code, description)),
Err(source) => {
let error = ParseInt::new(code, source, line!(), file!().to_owned());
Err(error.into())
}
}
}
_ => {
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
} else {
let error = KeyMissing::new(key, line!(), file!().to_string());
Err(error.into())
}
}
}
impl TryFrom<&serde_json::Value> for ApiError {
type Error = BeaErr;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
tracing::trace!("Reading ApiError.");
match value {
serde_json::Value::Object(m) => ApiError::read_json(m),
_ => {
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
}
}
#[derive(
Clone,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Deserialize,
serde::Serialize,
derive_new::new,
derive_more::Display,
)]
#[display("MNE API Error - Number: {number} - Error: {error}")]
pub struct MneError {
number: i32,
error: String,
}
impl MneError {
pub fn read_json(mp: &serde_json::Map<String, serde_json::Value>) -> Result<Self, BeaErr> {
let key = "Error".to_string();
if let Some(value) = mp.get(&key) {
match value {
serde_json::Value::Object(m) => {
let error = map_to_string("error", m)?;
let number = map_to_string("number", m)?;
match str::parse::<i32>(&number) {
Ok(number) => Ok(Self::new(number, error)),
Err(source) => {
let error = ParseInt::new(number, source, line!(), file!().to_owned());
Err(error.into())
}
}
}
serde_json::Value::Array(v) => {
tracing::trace!("Array found for {key}.");
if let Some(serde_json::Value::Object(m)) = v.clone().pop() {
let error = map_to_string("error", &m)?;
let number = map_to_string("number", &m)?;
match str::parse::<i32>(&number) {
Ok(number) => Ok(Self::new(number, error)),
Err(source) => {
let error = ParseInt::new(number, source, line!(), file!().into());
Err(error.into())
}
}
} else {
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
_ => {
tracing::trace!("Invalid Value: {value:#?}");
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
} else {
let error = KeyMissing::new(key, line!(), file!().to_string());
Err(error.into())
}
}
}
impl TryFrom<&serde_json::Value> for MneError {
type Error = BeaErr;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
tracing::trace!("Reading MneError.");
match value {
serde_json::Value::Object(m) => MneError::read_json(m),
_ => {
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
}
}
#[derive(
Clone,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
serde::Deserialize,
serde::Serialize,
derive_new::new,
derive_more::Display,
)]
#[display("API Error - Code: {code} - Description: {description}")]
pub struct RequestsExceeded {
code: i32,
description: String,
}
impl RequestsExceeded {
pub fn read_json(mp: &serde_json::Map<String, serde_json::Value>) -> Result<Self, BeaErr> {
let key = "Error".to_string();
if let Some(value) = mp.get(&key) {
match value {
serde_json::Value::Object(m) => {
let description = map_to_string("@APIErrorDescription", m)?;
let code = map_to_string("@APIErrorCode", m)?;
match str::parse::<i32>(&code) {
Ok(code) => Ok(Self::new(code, description)),
Err(source) => {
let error = ParseInt::new(code, source, line!(), file!().to_owned());
Err(error.into())
}
}
}
_ => {
tracing::trace!("Invalid Value: {value:#?}");
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
} else {
let error = KeyMissing::new(key, line!(), file!().to_string());
Err(error.into())
}
}
}
impl TryFrom<&serde_json::Value> for RequestsExceeded {
type Error = BeaErr;
fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
tracing::trace!("Reading RequestsExceeded.");
match value {
serde_json::Value::Object(m) => RequestsExceeded::read_json(m),
_ => {
let error = NotObject::new(line!(), file!().to_string());
Err(error.into())
}
}
}
}