1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::str::FromStr;
8
9use super::tag::Tag;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "lowercase")]
14pub enum ODPSApiVersion {
15 #[serde(rename = "v0.9.0")]
16 V0_9_0,
17 #[serde(rename = "v1.0.0")]
18 V1_0_0,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23pub enum ODPSStatus {
24 #[serde(rename = "proposed")]
25 Proposed,
26 #[serde(rename = "draft")]
27 Draft,
28 #[serde(rename = "active")]
29 Active,
30 #[serde(rename = "deprecated")]
31 Deprecated,
32 #[serde(rename = "retired")]
33 Retired,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
38#[serde(rename_all = "camelCase")]
39pub struct ODPSAuthoritativeDefinition {
40 pub r#type: String,
42 pub url: String,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub description: Option<String>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
51#[serde(rename_all = "camelCase")]
52pub struct ODPSCustomProperty {
53 pub property: String,
55 pub value: serde_json::Value,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub description: Option<String>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
64#[serde(rename_all = "camelCase")]
65pub struct ODPSDescription {
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub purpose: Option<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub limitations: Option<String>,
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub usage: Option<String>,
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
78 #[serde(skip_serializing_if = "Option::is_none")]
80 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
85#[serde(rename_all = "camelCase")]
86pub struct ODPSInputPort {
87 pub name: String,
89 pub version: String,
91 pub contract_id: String,
93 #[serde(
95 default,
96 skip_serializing_if = "Vec::is_empty",
97 deserialize_with = "deserialize_tags"
98 )]
99 pub tags: Vec<Tag>,
100 #[serde(skip_serializing_if = "Option::is_none")]
102 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
103 #[serde(skip_serializing_if = "Option::is_none")]
105 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
110#[serde(rename_all = "camelCase")]
111pub struct ODPSSBOM {
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub r#type: Option<String>,
115 pub url: String,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121#[serde(rename_all = "camelCase")]
122pub struct ODPSInputContract {
123 pub id: String,
125 pub version: String,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131#[serde(rename_all = "camelCase")]
132pub struct ODPSOutputPort {
133 pub name: String,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub description: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub r#type: Option<String>,
141 pub version: String,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub contract_id: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub sbom: Option<Vec<ODPSSBOM>>,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub input_contracts: Option<Vec<ODPSInputContract>>,
152 #[serde(
154 default,
155 skip_serializing_if = "Vec::is_empty",
156 deserialize_with = "deserialize_tags"
157 )]
158 pub tags: Vec<Tag>,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
169#[serde(rename_all = "camelCase")]
170pub struct ODPSManagementPort {
171 pub name: String,
173 pub content: String,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub r#type: Option<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub url: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub channel: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub description: Option<String>,
187 #[serde(
189 default,
190 skip_serializing_if = "Vec::is_empty",
191 deserialize_with = "deserialize_tags"
192 )]
193 pub tags: Vec<Tag>,
194 #[serde(skip_serializing_if = "Option::is_none")]
196 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
197 #[serde(skip_serializing_if = "Option::is_none")]
199 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
204#[serde(rename_all = "camelCase")]
205pub struct ODPSSupport {
206 pub channel: String,
208 pub url: String,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub description: Option<String>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub tool: Option<String>,
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub scope: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub invitation_url: Option<String>,
222 #[serde(
224 default,
225 skip_serializing_if = "Vec::is_empty",
226 deserialize_with = "deserialize_tags"
227 )]
228 pub tags: Vec<Tag>,
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
239#[serde(rename_all = "camelCase")]
240pub struct ODPSTeamMember {
241 pub username: String,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub name: Option<String>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub description: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub role: Option<String>,
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub date_in: Option<String>,
255 #[serde(skip_serializing_if = "Option::is_none")]
257 pub date_out: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub replaced_by_username: Option<String>,
261 #[serde(
263 default,
264 skip_serializing_if = "Vec::is_empty",
265 deserialize_with = "deserialize_tags"
266 )]
267 pub tags: Vec<Tag>,
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
271 #[serde(skip_serializing_if = "Option::is_none")]
273 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
278#[serde(rename_all = "camelCase")]
279pub struct ODPSTeam {
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub name: Option<String>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub description: Option<String>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub members: Option<Vec<ODPSTeamMember>>,
289 #[serde(
291 default,
292 skip_serializing_if = "Vec::is_empty",
293 deserialize_with = "deserialize_tags"
294 )]
295 pub tags: Vec<Tag>,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
306#[serde(rename_all = "camelCase")]
307pub struct ODPSDataProduct {
308 pub api_version: String,
310 pub kind: String,
312 pub id: String,
314 #[serde(skip_serializing_if = "Option::is_none")]
316 pub name: Option<String>,
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub version: Option<String>,
320 pub status: ODPSStatus,
322 #[serde(skip_serializing_if = "Option::is_none")]
324 pub domain: Option<String>,
325 #[serde(skip_serializing_if = "Option::is_none")]
327 pub tenant: Option<String>,
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub authoritative_definitions: Option<Vec<ODPSAuthoritativeDefinition>>,
331 #[serde(skip_serializing_if = "Option::is_none")]
333 pub description: Option<ODPSDescription>,
334 #[serde(skip_serializing_if = "Option::is_none")]
336 pub custom_properties: Option<Vec<ODPSCustomProperty>>,
337 #[serde(
339 default,
340 skip_serializing_if = "Vec::is_empty",
341 deserialize_with = "deserialize_tags"
342 )]
343 pub tags: Vec<Tag>,
344 #[serde(skip_serializing_if = "Option::is_none")]
346 pub input_ports: Option<Vec<ODPSInputPort>>,
347 #[serde(skip_serializing_if = "Option::is_none")]
349 pub output_ports: Option<Vec<ODPSOutputPort>>,
350 #[serde(skip_serializing_if = "Option::is_none")]
352 pub management_ports: Option<Vec<ODPSManagementPort>>,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub support: Option<Vec<ODPSSupport>>,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub team: Option<ODPSTeam>,
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub product_created_ts: Option<String>,
362 #[serde(skip_serializing_if = "Option::is_none")]
364 pub created_at: Option<DateTime<Utc>>,
365 #[serde(skip_serializing_if = "Option::is_none")]
367 pub updated_at: Option<DateTime<Utc>>,
368}
369
370fn deserialize_tags<'de, D>(deserializer: D) -> Result<Vec<Tag>, D::Error>
372where
373 D: serde::Deserializer<'de>,
374{
375 struct TagVisitor;
377
378 impl<'de> serde::de::Visitor<'de> for TagVisitor {
379 type Value = Vec<Tag>;
380
381 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
382 formatter.write_str("a vector of tags (strings or Tag objects)")
383 }
384
385 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
386 where
387 A: serde::de::SeqAccess<'de>,
388 {
389 let mut tags = Vec::new();
390 while let Some(item) = seq.next_element::<serde_json::Value>()? {
391 match item {
392 serde_json::Value::String(s) => {
393 if let Ok(tag) = Tag::from_str(&s) {
395 tags.push(tag);
396 }
397 }
398 _ => {
399 if let serde_json::Value::String(s) = item
401 && let Ok(tag) = Tag::from_str(&s)
402 {
403 tags.push(tag);
404 }
405 }
406 }
407 }
408 Ok(tags)
409 }
410 }
411
412 deserializer.deserialize_seq(TagVisitor)
413}