use crate::structure::TimeDataType;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use serde_with::serde_as;
use std::collections::HashMap;
use std::convert::Infallible;
use std::str::FromStr;
pub trait Extendable {
fn other(&self) -> Option<&HashMap<String, Value>>;
}
pub trait SdmxMessage {
type Data;
fn meta(&self) -> Option<&Meta>;
fn data(&self) -> Option<&Self::Data>;
fn errors(&self) -> Option<&Vec<StatusMessage>>;
}
pub type LocalizedText = HashMap<String, String>;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct Link {
#[serde(flatten)]
pub location: Location,
pub rel: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub titles: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hreflang: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum Location {
Href(String),
Urn(String),
}
impl Location {
pub fn as_string(&self) -> &String {
match self {
Self::Href(s) | Self::Urn(s) => s,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Action {
Append,
Replace,
Delete,
#[default]
Information,
}
impl TryFrom<char> for Action {
type Error = ();
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'A' => Ok(Self::Append),
'R' => Ok(Self::Replace),
'D' => Ok(Self::Delete),
'I' => Ok(Self::Information),
_ => Err(()),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub struct Annotation {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub type_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub texts: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<Vec<Link>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Contact {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub department: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub departments: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub roles: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub telephones: Option<Vec<String>>,
pub faxes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uris: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub emails: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub x400s: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum DataType {
String,
Alpha,
AlphaNumeric,
Numeric,
BigInteger,
Integer,
Long,
Short,
Decimal,
Float,
Double,
Boolean,
#[serde(rename = "URI")]
Uri,
Count,
InclusiveValueRange,
ExclusiveValueRange,
Incremental,
ObservationalTimePeriod,
StandardTimePeriod,
BasicTimePeriod,
GregorianTimePeriod,
GregorianYear,
GregorianYearMonth,
GregorianDay,
ReportingTimePeriod,
ReportingYear,
ReportingSemester,
ReportingTrimester,
ReportingQuarter,
ReportingMonth,
ReportingWeek,
ReportingDay,
DateTime,
TimeRange,
Month,
MonthDay,
Day,
Time,
Duration,
GeospatialInformation,
#[serde(rename = "XHTML")]
Xhtml,
}
impl DataType {
pub const fn is_reporting(&self) -> bool {
matches!(
self,
Self::ReportingDay
| Self::ReportingWeek
| Self::ReportingMonth
| Self::ReportingYear
| Self::ReportingQuarter
| Self::ReportingSemester
| Self::ReportingTrimester
| Self::ReportingTimePeriod
)
}
pub const fn is_gregorian(&self) -> bool {
matches!(
self,
Self::GregorianTimePeriod
| Self::GregorianYear
| Self::GregorianYearMonth
| Self::GregorianDay
)
}
}
impl From<TimeDataType> for DataType {
fn from(value: TimeDataType) -> Self {
match value {
TimeDataType::ObservationalTimePeriod => Self::ObservationalTimePeriod,
TimeDataType::StandardTimePeriod => Self::StandardTimePeriod,
TimeDataType::BasicTimePeriod => Self::BasicTimePeriod,
TimeDataType::GregorianTimePeriod => Self::GregorianTimePeriod,
TimeDataType::GregorianYear => Self::GregorianYear,
TimeDataType::GregorianYearMonth => Self::GregorianYearMonth,
TimeDataType::GregorianDay => Self::GregorianDay,
TimeDataType::ReportingTimePeriod => Self::ReportingTimePeriod,
TimeDataType::ReportingYear => Self::ReportingYear,
TimeDataType::ReportingSemester => Self::ReportingSemester,
TimeDataType::ReportingTrimester => Self::ReportingTrimester,
TimeDataType::ReportingQuarter => Self::ReportingQuarter,
TimeDataType::ReportingMonth => Self::ReportingMonth,
TimeDataType::ReportingWeek => Self::ReportingWeek,
TimeDataType::ReportingDay => Self::ReportingDay,
TimeDataType::DateTime => Self::DateTime,
TimeDataType::TimeRange => Self::TimeRange,
}
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct StatusMessage {
pub code: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub titles: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub links: Option<Vec<Link>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase")]
pub struct Party {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contacts: Option<Vec<Contact>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
pub type Sender = Party;
pub type Receiver = Party;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[serde_as]
#[serde(rename_all = "camelCase")]
pub struct Meta {
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<String>,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub test: Option<bool>,
pub prepared: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_languages: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<LocalizedText>,
pub sender: Sender,
#[serde_as(as = "OneOrMany<_, PreferOne>")]
#[serde(skip_serializing_if = "Option::is_none")]
pub receivers: Option<Vec<Receiver>>,
pub links: Option<Vec<Link>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum NumberOrString {
Number(isize),
String(String),
}
impl From<isize> for NumberOrString {
fn from(value: isize) -> Self {
Self::Number(value)
}
}
impl From<String> for NumberOrString {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<&str> for NumberOrString {
fn from(value: &str) -> Self {
Self::String(value.to_owned())
}
}
impl FromStr for NumberOrString {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::String(s.to_owned()))
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum SdmxValue {
Null,
String(String),
Integer(isize),
Number(f64),
Boolean(bool),
LocalizedText(LocalizedText),
Array(Box<Vec<SdmxValue>>),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct SdmxObject(pub HashMap<String, SdmxValue>);
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
#[serde(rename_all = "camelCase")]
pub struct SentinelValue {
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<NumberOrString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub names: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub descriptions: Option<LocalizedText>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(flatten)]
pub other: Option<HashMap<String, Value>>,
}
impl_extendable!(
Link,
Annotation,
Contact,
StatusMessage,
Party,
Meta,
SentinelValue,
);