1use crate::structure::TimeDataType;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use serde_with::serde_as;
5use std::collections::HashMap;
6use std::convert::Infallible;
7use std::str::FromStr;
8
9pub trait Extendable {
13 fn other(&self) -> Option<&HashMap<String, Value>>;
14}
15
16pub trait SdmxMessage {
19 type Data;
20 fn meta(&self) -> Option<&Meta>;
21 fn data(&self) -> Option<&Self::Data>;
22 fn errors(&self) -> Option<&Vec<StatusMessage>>;
23}
24
25pub type LocalizedText = HashMap<String, String>;
28
29#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
31pub struct Link {
32 #[serde(flatten)]
33 pub location: Location,
34 pub rel: String,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 pub url: Option<String>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub uri: Option<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub title: Option<String>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub titles: Option<LocalizedText>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 #[serde(rename = "type")]
45 pub type_: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub hreflang: Option<String>,
48 #[serde(skip_serializing_if = "Option::is_none")]
49 #[serde(flatten)]
50 pub other: Option<HashMap<String, Value>>,
51}
52
53#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
57#[serde(rename_all = "lowercase")]
58pub enum Location {
59 Href(String),
61 Urn(String),
63}
64
65impl Location {
66 pub fn as_string(&self) -> &String {
67 match self {
68 Self::Href(s) | Self::Urn(s) => s,
69 }
70 }
71}
72
73#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
76pub enum Action {
77 Append,
78 Replace,
79 Delete,
80 #[default]
81 Information,
82}
83
84impl TryFrom<char> for Action {
85 type Error = ();
86 fn try_from(value: char) -> Result<Self, Self::Error> {
87 match value {
88 'A' => Ok(Self::Append),
89 'R' => Ok(Self::Replace),
90 'D' => Ok(Self::Delete),
91 'I' => Ok(Self::Information),
92 _ => Err(()),
93 }
94 }
95}
96
97#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
99pub struct Annotation {
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub id: Option<String>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub title: Option<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 #[serde(rename = "type")]
106 pub type_: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub value: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub text: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub texts: Option<LocalizedText>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub links: Option<Vec<Link>>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 #[serde(flatten)]
117 pub other: Option<HashMap<String, Value>>,
118}
119
120#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
122#[serde(rename_all = "camelCase")]
123pub struct Contact {
124 pub id: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub name: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub names: Option<LocalizedText>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub department: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub departments: Option<LocalizedText>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub role: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub roles: Option<LocalizedText>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub telephones: Option<Vec<String>>,
139 pub faxes: Option<Vec<String>>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub uris: Option<Vec<String>>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub emails: Option<Vec<String>>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub x400s: Option<Vec<String>>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 #[serde(flatten)]
148 pub other: Option<HashMap<String, Value>>,
149}
150
151#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
153pub enum DataType {
154 String,
155 Alpha,
156 AlphaNumeric,
157 Numeric,
158 BigInteger,
159 Integer,
160 Long,
161 Short,
162 Decimal,
163 Float,
164 Double,
165 Boolean,
166 #[serde(rename = "URI")]
167 Uri,
168 Count,
169 InclusiveValueRange,
170 ExclusiveValueRange,
171 Incremental,
172 ObservationalTimePeriod,
173 StandardTimePeriod,
174 BasicTimePeriod,
175 GregorianTimePeriod,
176 GregorianYear,
177 GregorianYearMonth,
178 GregorianDay,
179 ReportingTimePeriod,
180 ReportingYear,
181 ReportingSemester,
182 ReportingTrimester,
183 ReportingQuarter,
184 ReportingMonth,
185 ReportingWeek,
186 ReportingDay,
187 DateTime,
188 TimeRange,
189 Month,
190 MonthDay,
191 Day,
192 Time,
193 Duration,
194 GeospatialInformation,
195 #[serde(rename = "XHTML")]
196 Xhtml,
197}
198
199impl DataType {
200 pub const fn is_reporting(&self) -> bool {
201 matches!(
202 self,
203 Self::ReportingDay
204 | Self::ReportingWeek
205 | Self::ReportingMonth
206 | Self::ReportingYear
207 | Self::ReportingQuarter
208 | Self::ReportingSemester
209 | Self::ReportingTrimester
210 | Self::ReportingTimePeriod
211 )
212 }
213
214 pub const fn is_gregorian(&self) -> bool {
215 matches!(
216 self,
217 Self::GregorianTimePeriod
218 | Self::GregorianYear
219 | Self::GregorianYearMonth
220 | Self::GregorianDay
221 )
222 }
223}
224
225impl From<TimeDataType> for DataType {
226 fn from(value: TimeDataType) -> Self {
227 match value {
228 TimeDataType::ObservationalTimePeriod => Self::ObservationalTimePeriod,
229 TimeDataType::StandardTimePeriod => Self::StandardTimePeriod,
230 TimeDataType::BasicTimePeriod => Self::BasicTimePeriod,
231 TimeDataType::GregorianTimePeriod => Self::GregorianTimePeriod,
232 TimeDataType::GregorianYear => Self::GregorianYear,
233 TimeDataType::GregorianYearMonth => Self::GregorianYearMonth,
234 TimeDataType::GregorianDay => Self::GregorianDay,
235 TimeDataType::ReportingTimePeriod => Self::ReportingTimePeriod,
236 TimeDataType::ReportingYear => Self::ReportingYear,
237 TimeDataType::ReportingSemester => Self::ReportingSemester,
238 TimeDataType::ReportingTrimester => Self::ReportingTrimester,
239 TimeDataType::ReportingQuarter => Self::ReportingQuarter,
240 TimeDataType::ReportingMonth => Self::ReportingMonth,
241 TimeDataType::ReportingWeek => Self::ReportingWeek,
242 TimeDataType::ReportingDay => Self::ReportingDay,
243 TimeDataType::DateTime => Self::DateTime,
244 TimeDataType::TimeRange => Self::TimeRange,
245 }
246 }
247}
248
249#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
251pub struct StatusMessage {
252 pub code: usize,
253 #[serde(skip_serializing_if = "Option::is_none")]
254 pub title: Option<String>,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub titles: Option<LocalizedText>,
257 #[serde(skip_serializing_if = "Option::is_none")]
258 pub detail: Option<String>,
259 #[serde(skip_serializing_if = "Option::is_none")]
260 pub details: Option<LocalizedText>,
261 #[serde(skip_serializing_if = "Option::is_none")]
262 pub links: Option<Vec<Link>>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 #[serde(flatten)]
265 pub other: Option<HashMap<String, Value>>,
266}
267
268#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
270#[serde(rename_all = "camelCase")]
271pub struct Party {
272 pub id: String,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub name: Option<String>,
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub names: Option<LocalizedText>,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub contacts: Option<Vec<Contact>>,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 #[serde(flatten)]
281 pub other: Option<HashMap<String, Value>>,
282}
283
284pub type Sender = Party;
286pub type Receiver = Party;
288
289#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
292#[serde_as]
293#[serde(rename_all = "camelCase")]
294pub struct Meta {
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub schema: Option<String>,
297 pub id: String,
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub test: Option<bool>,
300 pub prepared: String,
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub content_languages: Option<Vec<String>>,
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub name: Option<String>,
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub names: Option<LocalizedText>,
307 pub sender: Sender,
308 #[serde_as(as = "OneOrMany<_, PreferOne>")]
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub receivers: Option<Vec<Receiver>>,
311 pub links: Option<Vec<Link>>,
312 #[serde(skip_serializing_if = "Option::is_none")]
313 #[serde(flatten)]
314 pub other: Option<HashMap<String, Value>>,
315}
316
317#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
319pub enum NumberOrString {
320 Number(isize),
321 String(String),
322}
323
324impl From<isize> for NumberOrString {
325 fn from(value: isize) -> Self {
326 Self::Number(value)
327 }
328}
329
330impl From<String> for NumberOrString {
331 fn from(value: String) -> Self {
332 Self::String(value)
333 }
334}
335
336impl From<&str> for NumberOrString {
337 fn from(value: &str) -> Self {
338 Self::String(value.to_owned())
339 }
340}
341
342impl FromStr for NumberOrString {
343 type Err = Infallible;
344 fn from_str(s: &str) -> Result<Self, Self::Err> {
345 Ok(Self::String(s.to_owned()))
346 }
347}
348
349#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
354#[serde(untagged)]
355pub enum SdmxValue {
356 Null,
357 String(String),
358 Integer(isize),
359 Number(f64),
360 Boolean(bool),
361 LocalizedText(LocalizedText),
362 Array(Box<Vec<SdmxValue>>),
363}
364
365#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
367pub struct SdmxObject(pub HashMap<String, SdmxValue>);
368
369#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
372#[serde(rename_all = "camelCase")]
373pub struct SentinelValue {
374 #[serde(skip_serializing_if = "Option::is_none")]
375 pub value: Option<NumberOrString>,
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub name: Option<String>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub names: Option<LocalizedText>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub description: Option<String>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub descriptions: Option<LocalizedText>,
384 #[serde(skip_serializing_if = "Option::is_none")]
385 #[serde(flatten)]
386 pub other: Option<HashMap<String, Value>>,
387}
388
389impl_extendable!(
390 Link,
391 Annotation,
392 Contact,
393 StatusMessage,
394 Party,
395 Meta,
396 SentinelValue,
397);