1use crate::export::ExportError;
6use crate::models::odps::*;
7use serde_yaml;
8
9pub struct ODPSExporter;
11
12impl ODPSExporter {
13 pub fn export(&self, product: &ODPSDataProduct) -> Result<String, ExportError> {
23 let yaml = Self::export_product(product);
24
25 #[cfg(feature = "odps-validation")]
27 {
28 use crate::validation::schema::validate_odps_internal;
29 validate_odps_internal(&yaml).map_err(ExportError::ValidationError)?;
30 }
31
32 Ok(yaml)
33 }
34
35 pub fn export_product(product: &ODPSDataProduct) -> String {
79 let mut yaml = serde_yaml::Mapping::new();
80
81 yaml.insert(
83 serde_yaml::Value::String("apiVersion".to_string()),
84 serde_yaml::Value::String(product.api_version.clone()),
85 );
86
87 yaml.insert(
88 serde_yaml::Value::String("kind".to_string()),
89 serde_yaml::Value::String(product.kind.clone()),
90 );
91
92 yaml.insert(
93 serde_yaml::Value::String("id".to_string()),
94 serde_yaml::Value::String(product.id.clone()),
95 );
96
97 let status_str = match product.status {
98 ODPSStatus::Proposed => "proposed",
99 ODPSStatus::Draft => "draft",
100 ODPSStatus::Active => "active",
101 ODPSStatus::Deprecated => "deprecated",
102 ODPSStatus::Retired => "retired",
103 };
104 yaml.insert(
105 serde_yaml::Value::String("status".to_string()),
106 serde_yaml::Value::String(status_str.to_string()),
107 );
108
109 if let Some(name) = &product.name {
111 yaml.insert(
112 serde_yaml::Value::String("name".to_string()),
113 serde_yaml::Value::String(name.clone()),
114 );
115 }
116
117 if let Some(version) = &product.version {
118 yaml.insert(
119 serde_yaml::Value::String("version".to_string()),
120 serde_yaml::Value::String(version.clone()),
121 );
122 }
123
124 if let Some(domain) = &product.domain {
125 yaml.insert(
126 serde_yaml::Value::String("domain".to_string()),
127 serde_yaml::Value::String(domain.clone()),
128 );
129 }
130
131 if let Some(tenant) = &product.tenant {
132 yaml.insert(
133 serde_yaml::Value::String("tenant".to_string()),
134 serde_yaml::Value::String(tenant.clone()),
135 );
136 }
137
138 if !product.tags.is_empty() {
139 let tags_yaml: Vec<serde_yaml::Value> = product
140 .tags
141 .iter()
142 .map(|t| serde_yaml::Value::String(t.to_string()))
143 .collect();
144 yaml.insert(
145 serde_yaml::Value::String("tags".to_string()),
146 serde_yaml::Value::Sequence(tags_yaml),
147 );
148 }
149
150 if let Some(description) = &product.description {
151 let mut desc_map = serde_yaml::Mapping::new();
152 if let Some(purpose) = &description.purpose {
153 desc_map.insert(
154 serde_yaml::Value::String("purpose".to_string()),
155 serde_yaml::Value::String(purpose.clone()),
156 );
157 }
158 if let Some(limitations) = &description.limitations {
159 desc_map.insert(
160 serde_yaml::Value::String("limitations".to_string()),
161 serde_yaml::Value::String(limitations.clone()),
162 );
163 }
164 if let Some(usage) = &description.usage {
165 desc_map.insert(
166 serde_yaml::Value::String("usage".to_string()),
167 serde_yaml::Value::String(usage.clone()),
168 );
169 }
170 if let Some(auth_defs) = &description.authoritative_definitions {
171 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
172 if !defs_yaml.is_empty() {
173 desc_map.insert(
174 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
175 serde_yaml::Value::Sequence(defs_yaml),
176 );
177 }
178 }
179 if let Some(custom_props) = &description.custom_properties {
180 let props_yaml = Self::serialize_custom_properties(custom_props);
181 if !props_yaml.is_empty() {
182 desc_map.insert(
183 serde_yaml::Value::String("customProperties".to_string()),
184 serde_yaml::Value::Sequence(props_yaml),
185 );
186 }
187 }
188 if !desc_map.is_empty() {
189 yaml.insert(
190 serde_yaml::Value::String("description".to_string()),
191 serde_yaml::Value::Mapping(desc_map),
192 );
193 }
194 }
195
196 if let Some(auth_defs) = &product.authoritative_definitions {
197 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
199 yaml.insert(
200 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
201 serde_yaml::Value::Sequence(defs_yaml),
202 );
203 }
204
205 if let Some(custom_props) = &product.custom_properties {
206 let props_yaml = Self::serialize_custom_properties(custom_props);
208 yaml.insert(
209 serde_yaml::Value::String("customProperties".to_string()),
210 serde_yaml::Value::Sequence(props_yaml),
211 );
212 }
213
214 if let Some(input_ports) = &product.input_ports {
215 let ports_yaml: Vec<serde_yaml::Value> = input_ports
217 .iter()
218 .map(|port| {
219 let mut port_map = serde_yaml::Mapping::new();
220 port_map.insert(
221 serde_yaml::Value::String("name".to_string()),
222 serde_yaml::Value::String(port.name.clone()),
223 );
224 port_map.insert(
225 serde_yaml::Value::String("version".to_string()),
226 serde_yaml::Value::String(port.version.clone()),
227 );
228 port_map.insert(
229 serde_yaml::Value::String("contractId".to_string()),
230 serde_yaml::Value::String(port.contract_id.clone()),
231 );
232 if !port.tags.is_empty() {
233 let tags_yaml: Vec<serde_yaml::Value> = port
234 .tags
235 .iter()
236 .map(|t| serde_yaml::Value::String(t.to_string()))
237 .collect();
238 port_map.insert(
239 serde_yaml::Value::String("tags".to_string()),
240 serde_yaml::Value::Sequence(tags_yaml),
241 );
242 }
243 if let Some(custom_props) = &port.custom_properties {
244 let props_yaml = Self::serialize_custom_properties(custom_props);
245 if !props_yaml.is_empty() {
246 port_map.insert(
247 serde_yaml::Value::String("customProperties".to_string()),
248 serde_yaml::Value::Sequence(props_yaml),
249 );
250 }
251 }
252 if let Some(auth_defs) = &port.authoritative_definitions {
253 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
254 if !defs_yaml.is_empty() {
255 port_map.insert(
256 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
257 serde_yaml::Value::Sequence(defs_yaml),
258 );
259 }
260 }
261 serde_yaml::Value::Mapping(port_map)
262 })
263 .collect();
264 yaml.insert(
266 serde_yaml::Value::String("inputPorts".to_string()),
267 serde_yaml::Value::Sequence(ports_yaml),
268 );
269 }
270
271 if let Some(output_ports) = &product.output_ports {
272 let ports_yaml: Vec<serde_yaml::Value> = output_ports
273 .iter()
274 .map(|port| {
275 let mut port_map = serde_yaml::Mapping::new();
276 port_map.insert(
277 serde_yaml::Value::String("name".to_string()),
278 serde_yaml::Value::String(port.name.clone()),
279 );
280 port_map.insert(
281 serde_yaml::Value::String("version".to_string()),
282 serde_yaml::Value::String(port.version.clone()),
283 );
284 if let Some(description) = &port.description {
285 port_map.insert(
286 serde_yaml::Value::String("description".to_string()),
287 serde_yaml::Value::String(description.clone()),
288 );
289 }
290 if let Some(r#type) = &port.r#type {
291 port_map.insert(
292 serde_yaml::Value::String("type".to_string()),
293 serde_yaml::Value::String(r#type.clone()),
294 );
295 }
296 if let Some(contract_id) = &port.contract_id {
297 port_map.insert(
298 serde_yaml::Value::String("contractId".to_string()),
299 serde_yaml::Value::String(contract_id.clone()),
300 );
301 }
302 if let Some(sbom) = &port.sbom {
303 let sbom_yaml: Vec<serde_yaml::Value> = sbom
304 .iter()
305 .map(|s| {
306 let mut sbom_map = serde_yaml::Mapping::new();
307 sbom_map.insert(
308 serde_yaml::Value::String("url".to_string()),
309 serde_yaml::Value::String(s.url.clone()),
310 );
311 if let Some(r#type) = &s.r#type {
312 sbom_map.insert(
313 serde_yaml::Value::String("type".to_string()),
314 serde_yaml::Value::String(r#type.clone()),
315 );
316 }
317 serde_yaml::Value::Mapping(sbom_map)
318 })
319 .collect();
320 port_map.insert(
321 serde_yaml::Value::String("sbom".to_string()),
322 serde_yaml::Value::Sequence(sbom_yaml),
323 );
324 }
325 if let Some(input_contracts) = &port.input_contracts {
326 let contracts_yaml: Vec<serde_yaml::Value> = input_contracts
327 .iter()
328 .map(|contract| {
329 let mut contract_map = serde_yaml::Mapping::new();
330 contract_map.insert(
331 serde_yaml::Value::String("id".to_string()),
332 serde_yaml::Value::String(contract.id.clone()),
333 );
334 contract_map.insert(
335 serde_yaml::Value::String("version".to_string()),
336 serde_yaml::Value::String(contract.version.clone()),
337 );
338 serde_yaml::Value::Mapping(contract_map)
339 })
340 .collect();
341 port_map.insert(
342 serde_yaml::Value::String("inputContracts".to_string()),
343 serde_yaml::Value::Sequence(contracts_yaml),
344 );
345 }
346 if !port.tags.is_empty() {
347 let tags_yaml: Vec<serde_yaml::Value> = port
348 .tags
349 .iter()
350 .map(|t| serde_yaml::Value::String(t.to_string()))
351 .collect();
352 port_map.insert(
353 serde_yaml::Value::String("tags".to_string()),
354 serde_yaml::Value::Sequence(tags_yaml),
355 );
356 }
357 if let Some(custom_props) = &port.custom_properties {
358 let props_yaml = Self::serialize_custom_properties(custom_props);
359 if !props_yaml.is_empty() {
360 port_map.insert(
361 serde_yaml::Value::String("customProperties".to_string()),
362 serde_yaml::Value::Sequence(props_yaml),
363 );
364 }
365 }
366 if let Some(auth_defs) = &port.authoritative_definitions {
367 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
368 if !defs_yaml.is_empty() {
369 port_map.insert(
370 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
371 serde_yaml::Value::Sequence(defs_yaml),
372 );
373 }
374 }
375 serde_yaml::Value::Mapping(port_map)
376 })
377 .collect();
378 yaml.insert(
379 serde_yaml::Value::String("outputPorts".to_string()),
380 serde_yaml::Value::Sequence(ports_yaml),
381 );
382 }
383
384 if let Some(management_ports) = &product.management_ports {
385 let ports_yaml: Vec<serde_yaml::Value> = management_ports
386 .iter()
387 .map(|port| {
388 let mut port_map = serde_yaml::Mapping::new();
389 port_map.insert(
390 serde_yaml::Value::String("name".to_string()),
391 serde_yaml::Value::String(port.name.clone()),
392 );
393 port_map.insert(
394 serde_yaml::Value::String("content".to_string()),
395 serde_yaml::Value::String(port.content.clone()),
396 );
397 if let Some(r#type) = &port.r#type {
398 port_map.insert(
399 serde_yaml::Value::String("type".to_string()),
400 serde_yaml::Value::String(r#type.clone()),
401 );
402 }
403 if let Some(url) = &port.url {
404 port_map.insert(
405 serde_yaml::Value::String("url".to_string()),
406 serde_yaml::Value::String(url.clone()),
407 );
408 }
409 if let Some(channel) = &port.channel {
410 port_map.insert(
411 serde_yaml::Value::String("channel".to_string()),
412 serde_yaml::Value::String(channel.clone()),
413 );
414 }
415 if let Some(description) = &port.description {
416 port_map.insert(
417 serde_yaml::Value::String("description".to_string()),
418 serde_yaml::Value::String(description.clone()),
419 );
420 }
421 if !port.tags.is_empty() {
422 let tags_yaml: Vec<serde_yaml::Value> = port
423 .tags
424 .iter()
425 .map(|t| serde_yaml::Value::String(t.to_string()))
426 .collect();
427 port_map.insert(
428 serde_yaml::Value::String("tags".to_string()),
429 serde_yaml::Value::Sequence(tags_yaml),
430 );
431 }
432 if let Some(custom_props) = &port.custom_properties {
433 let props_yaml = Self::serialize_custom_properties(custom_props);
434 if !props_yaml.is_empty() {
435 port_map.insert(
436 serde_yaml::Value::String("customProperties".to_string()),
437 serde_yaml::Value::Sequence(props_yaml),
438 );
439 }
440 }
441 if let Some(auth_defs) = &port.authoritative_definitions {
442 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
443 if !defs_yaml.is_empty() {
444 port_map.insert(
445 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
446 serde_yaml::Value::Sequence(defs_yaml),
447 );
448 }
449 }
450 serde_yaml::Value::Mapping(port_map)
451 })
452 .collect();
453 yaml.insert(
454 serde_yaml::Value::String("managementPorts".to_string()),
455 serde_yaml::Value::Sequence(ports_yaml),
456 );
457 }
458
459 if let Some(support) = &product.support {
460 let support_yaml: Vec<serde_yaml::Value> = support
461 .iter()
462 .map(|s| {
463 let mut support_map = serde_yaml::Mapping::new();
464 support_map.insert(
465 serde_yaml::Value::String("channel".to_string()),
466 serde_yaml::Value::String(s.channel.clone()),
467 );
468 support_map.insert(
469 serde_yaml::Value::String("url".to_string()),
470 serde_yaml::Value::String(s.url.clone()),
471 );
472 if let Some(description) = &s.description {
473 support_map.insert(
474 serde_yaml::Value::String("description".to_string()),
475 serde_yaml::Value::String(description.clone()),
476 );
477 }
478 if let Some(tool) = &s.tool {
479 support_map.insert(
480 serde_yaml::Value::String("tool".to_string()),
481 serde_yaml::Value::String(tool.clone()),
482 );
483 }
484 if let Some(scope) = &s.scope {
485 support_map.insert(
486 serde_yaml::Value::String("scope".to_string()),
487 serde_yaml::Value::String(scope.clone()),
488 );
489 }
490 if let Some(invitation_url) = &s.invitation_url {
491 support_map.insert(
492 serde_yaml::Value::String("invitationUrl".to_string()),
493 serde_yaml::Value::String(invitation_url.clone()),
494 );
495 }
496 if !s.tags.is_empty() {
497 let tags_yaml: Vec<serde_yaml::Value> = s
498 .tags
499 .iter()
500 .map(|t| serde_yaml::Value::String(t.to_string()))
501 .collect();
502 support_map.insert(
503 serde_yaml::Value::String("tags".to_string()),
504 serde_yaml::Value::Sequence(tags_yaml),
505 );
506 }
507 if let Some(custom_props) = &s.custom_properties {
508 let props_yaml = Self::serialize_custom_properties(custom_props);
509 if !props_yaml.is_empty() {
510 support_map.insert(
511 serde_yaml::Value::String("customProperties".to_string()),
512 serde_yaml::Value::Sequence(props_yaml),
513 );
514 }
515 }
516 if let Some(auth_defs) = &s.authoritative_definitions {
517 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
518 if !defs_yaml.is_empty() {
519 support_map.insert(
520 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
521 serde_yaml::Value::Sequence(defs_yaml),
522 );
523 }
524 }
525 serde_yaml::Value::Mapping(support_map)
526 })
527 .collect();
528 yaml.insert(
529 serde_yaml::Value::String("support".to_string()),
530 serde_yaml::Value::Sequence(support_yaml),
531 );
532 }
533
534 if let Some(team) = &product.team {
535 let mut team_map = serde_yaml::Mapping::new();
536 if let Some(name) = &team.name {
537 team_map.insert(
538 serde_yaml::Value::String("name".to_string()),
539 serde_yaml::Value::String(name.clone()),
540 );
541 }
542 if let Some(description) = &team.description {
543 team_map.insert(
544 serde_yaml::Value::String("description".to_string()),
545 serde_yaml::Value::String(description.clone()),
546 );
547 }
548 if let Some(members) = &team.members {
549 let members_yaml: Vec<serde_yaml::Value> = members
550 .iter()
551 .map(|member| {
552 let mut member_map = serde_yaml::Mapping::new();
553 member_map.insert(
554 serde_yaml::Value::String("username".to_string()),
555 serde_yaml::Value::String(member.username.clone()),
556 );
557 if let Some(name) = &member.name {
558 member_map.insert(
559 serde_yaml::Value::String("name".to_string()),
560 serde_yaml::Value::String(name.clone()),
561 );
562 }
563 if let Some(description) = &member.description {
564 member_map.insert(
565 serde_yaml::Value::String("description".to_string()),
566 serde_yaml::Value::String(description.clone()),
567 );
568 }
569 if let Some(role) = &member.role {
570 member_map.insert(
571 serde_yaml::Value::String("role".to_string()),
572 serde_yaml::Value::String(role.clone()),
573 );
574 }
575 if let Some(date_in) = &member.date_in {
576 member_map.insert(
577 serde_yaml::Value::String("dateIn".to_string()),
578 serde_yaml::Value::String(date_in.clone()),
579 );
580 }
581 if let Some(date_out) = &member.date_out {
582 member_map.insert(
583 serde_yaml::Value::String("dateOut".to_string()),
584 serde_yaml::Value::String(date_out.clone()),
585 );
586 }
587 if let Some(replaced_by) = &member.replaced_by_username {
588 member_map.insert(
589 serde_yaml::Value::String("replacedByUsername".to_string()),
590 serde_yaml::Value::String(replaced_by.clone()),
591 );
592 }
593 if !member.tags.is_empty() {
594 let tags_yaml: Vec<serde_yaml::Value> = member
595 .tags
596 .iter()
597 .map(|t| serde_yaml::Value::String(t.to_string()))
598 .collect();
599 member_map.insert(
600 serde_yaml::Value::String("tags".to_string()),
601 serde_yaml::Value::Sequence(tags_yaml),
602 );
603 }
604 if let Some(custom_props) = &member.custom_properties {
605 let props_yaml = Self::serialize_custom_properties(custom_props);
606 if !props_yaml.is_empty() {
607 member_map.insert(
608 serde_yaml::Value::String("customProperties".to_string()),
609 serde_yaml::Value::Sequence(props_yaml),
610 );
611 }
612 }
613 if let Some(auth_defs) = &member.authoritative_definitions {
614 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
615 if !defs_yaml.is_empty() {
616 member_map.insert(
617 serde_yaml::Value::String(
618 "authoritativeDefinitions".to_string(),
619 ),
620 serde_yaml::Value::Sequence(defs_yaml),
621 );
622 }
623 }
624 serde_yaml::Value::Mapping(member_map)
625 })
626 .collect();
627 team_map.insert(
628 serde_yaml::Value::String("members".to_string()),
629 serde_yaml::Value::Sequence(members_yaml),
630 );
631 }
632 if !team.tags.is_empty() {
633 let tags_yaml: Vec<serde_yaml::Value> = team
634 .tags
635 .iter()
636 .map(|t| serde_yaml::Value::String(t.to_string()))
637 .collect();
638 team_map.insert(
639 serde_yaml::Value::String("tags".to_string()),
640 serde_yaml::Value::Sequence(tags_yaml),
641 );
642 }
643 if let Some(custom_props) = &team.custom_properties {
644 let props_yaml = Self::serialize_custom_properties(custom_props);
645 if !props_yaml.is_empty() {
646 team_map.insert(
647 serde_yaml::Value::String("customProperties".to_string()),
648 serde_yaml::Value::Sequence(props_yaml),
649 );
650 }
651 }
652 if let Some(auth_defs) = &team.authoritative_definitions {
653 let defs_yaml = Self::serialize_authoritative_definitions(auth_defs);
654 if !defs_yaml.is_empty() {
655 team_map.insert(
656 serde_yaml::Value::String("authoritativeDefinitions".to_string()),
657 serde_yaml::Value::Sequence(defs_yaml),
658 );
659 }
660 }
661 if !team_map.is_empty() {
662 yaml.insert(
663 serde_yaml::Value::String("team".to_string()),
664 serde_yaml::Value::Mapping(team_map),
665 );
666 }
667 }
668
669 if let Some(product_created_ts) = &product.product_created_ts {
670 yaml.insert(
671 serde_yaml::Value::String("productCreatedTs".to_string()),
672 serde_yaml::Value::String(product_created_ts.clone()),
673 );
674 }
675
676 serde_yaml::to_string(&serde_yaml::Value::Mapping(yaml))
678 .unwrap_or_else(|_| String::from(""))
679 }
680
681 fn serialize_authoritative_definitions(
683 defs: &[ODPSAuthoritativeDefinition],
684 ) -> Vec<serde_yaml::Value> {
685 defs.iter()
686 .map(|def| {
687 let mut def_map = serde_yaml::Mapping::new();
688 def_map.insert(
689 serde_yaml::Value::String("type".to_string()),
690 serde_yaml::Value::String(def.r#type.clone()),
691 );
692 def_map.insert(
693 serde_yaml::Value::String("url".to_string()),
694 serde_yaml::Value::String(def.url.clone()),
695 );
696 if let Some(description) = &def.description {
697 def_map.insert(
698 serde_yaml::Value::String("description".to_string()),
699 serde_yaml::Value::String(description.clone()),
700 );
701 }
702 serde_yaml::Value::Mapping(def_map)
703 })
704 .collect()
705 }
706
707 fn serialize_custom_properties(props: &[ODPSCustomProperty]) -> Vec<serde_yaml::Value> {
709 props
710 .iter()
711 .map(|prop| {
712 let mut prop_map = serde_yaml::Mapping::new();
713 prop_map.insert(
714 serde_yaml::Value::String("property".to_string()),
715 serde_yaml::Value::String(prop.property.clone()),
716 );
717 prop_map.insert(
718 serde_yaml::Value::String("value".to_string()),
719 Self::json_to_yaml_value(&prop.value),
720 );
721 if let Some(description) = &prop.description {
722 prop_map.insert(
723 serde_yaml::Value::String("description".to_string()),
724 serde_yaml::Value::String(description.clone()),
725 );
726 }
727 serde_yaml::Value::Mapping(prop_map)
728 })
729 .collect()
730 }
731
732 fn json_to_yaml_value(json: &serde_json::Value) -> serde_yaml::Value {
734 match json {
735 serde_json::Value::Null => serde_yaml::Value::Null,
736 serde_json::Value::Bool(b) => serde_yaml::Value::Bool(*b),
737 serde_json::Value::Number(n) => {
738 if let Some(i) = n.as_i64() {
739 serde_yaml::Value::Number(serde_yaml::Number::from(i))
740 } else if let Some(f) = n.as_f64() {
741 serde_yaml::Value::Number(serde_yaml::Number::from(f))
742 } else {
743 serde_yaml::Value::String(n.to_string())
744 }
745 }
746 serde_json::Value::String(s) => serde_yaml::Value::String(s.clone()),
747 serde_json::Value::Array(arr) => {
748 let yaml_arr: Vec<serde_yaml::Value> =
749 arr.iter().map(Self::json_to_yaml_value).collect();
750 serde_yaml::Value::Sequence(yaml_arr)
751 }
752 serde_json::Value::Object(obj) => {
753 let mut yaml_map = serde_yaml::Mapping::new();
754 for (k, v) in obj {
755 yaml_map.insert(
756 serde_yaml::Value::String(k.clone()),
757 Self::json_to_yaml_value(v),
758 );
759 }
760 serde_yaml::Value::Mapping(yaml_map)
761 }
762 }
763 }
764}