1pub mod auth;
12pub mod convert;
13pub mod export;
14#[cfg(feature = "git")]
15pub mod git;
16pub mod import;
17pub mod model;
18pub mod models;
19pub mod storage;
20pub mod validation;
21pub mod workspace;
22
23#[cfg(feature = "api-backend")]
25pub use storage::api::ApiStorageBackend;
26#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
27pub use storage::browser::BrowserStorageBackend;
28#[cfg(feature = "native-fs")]
29pub use storage::filesystem::FileSystemStorageBackend;
30pub use storage::{StorageBackend, StorageError};
31
32pub use convert::{ConversionError, convert_to_odcs};
33#[cfg(feature = "png-export")]
34pub use export::PNGExporter;
35pub use export::{
36 AvroExporter, ExportError, ExportResult, JSONSchemaExporter, ODCSExporter, ProtobufExporter,
37 SQLExporter,
38};
39pub use import::{
40 AvroImporter, ImportError, ImportResult, JSONSchemaImporter, ODCSImporter, ProtobufImporter,
41 SQLImporter,
42};
43#[cfg(feature = "api-backend")]
44pub use model::ApiModelLoader;
45pub use model::{ModelLoader, ModelSaver};
46pub use validation::{
47 RelationshipValidationError, RelationshipValidationResult, TableValidationError,
48 TableValidationResult,
49};
50
51pub use models::enums::*;
53pub use models::{Column, ContactDetails, DataModel, ForeignKey, Relationship, SlaProperty, Table};
54
55pub use auth::{
57 AuthMode, AuthState, GitHubEmail, InitiateOAuthRequest, InitiateOAuthResponse,
58 SelectEmailRequest,
59};
60
61pub use workspace::{
63 CreateWorkspaceRequest, CreateWorkspaceResponse, ListProfilesResponse, LoadProfileRequest,
64 ProfileInfo, WorkspaceInfo,
65};
66
67#[cfg(feature = "git")]
69pub use git::{GitError, GitService, GitStatus};
70
71#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
73mod wasm {
74 use crate::export::ExportError;
75 use crate::import::{ImportError, ImportResult};
76 use crate::models::DataModel;
77 use js_sys;
78 use serde_json;
79 use serde_yaml;
80 use uuid;
81 use wasm_bindgen::prelude::*;
82 use wasm_bindgen_futures;
83
84 fn import_error_to_js(err: ImportError) -> JsValue {
86 JsValue::from_str(&err.to_string())
87 }
88
89 fn export_error_to_js(err: ExportError) -> JsValue {
91 JsValue::from_str(&err.to_string())
92 }
93
94 fn serialize_import_result(result: &ImportResult) -> Result<String, JsValue> {
96 serde_json::to_string(result)
97 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
98 }
99
100 fn deserialize_workspace(json: &str) -> Result<DataModel, JsValue> {
102 serde_json::from_str(json)
103 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))
104 }
105
106 #[wasm_bindgen]
116 pub fn parse_odcs_yaml(yaml_content: &str) -> Result<String, JsValue> {
117 let mut importer = crate::import::ODCSImporter::new();
118 match importer.import(yaml_content) {
119 Ok(result) => serialize_import_result(&result),
120 Err(err) => Err(import_error_to_js(err)),
121 }
122 }
123
124 #[wasm_bindgen]
134 pub fn export_to_odcs_yaml(workspace_json: &str) -> Result<String, JsValue> {
135 let model = deserialize_workspace(workspace_json)?;
136
137 let exports = crate::export::ODCSExporter::export_model(&model, None, "odcs_v3_1_0");
139
140 let yaml_docs: Vec<String> = exports.values().cloned().collect();
142 Ok(yaml_docs.join("\n---\n"))
143 }
144
145 #[wasm_bindgen]
156 pub fn import_from_sql(sql_content: &str, dialect: &str) -> Result<String, JsValue> {
157 let importer = crate::import::SQLImporter::new(dialect);
158 match importer.parse(sql_content) {
159 Ok(result) => serialize_import_result(&result),
160 Err(err) => Err(JsValue::from_str(&format!("Parse error: {}", err))),
161 }
162 }
163
164 #[wasm_bindgen]
174 pub fn import_from_avro(avro_content: &str) -> Result<String, JsValue> {
175 let importer = crate::import::AvroImporter::new();
176 match importer.import(avro_content) {
177 Ok(result) => serialize_import_result(&result),
178 Err(err) => Err(import_error_to_js(err)),
179 }
180 }
181
182 #[wasm_bindgen]
192 pub fn import_from_json_schema(json_schema_content: &str) -> Result<String, JsValue> {
193 let importer = crate::import::JSONSchemaImporter::new();
194 match importer.import(json_schema_content) {
195 Ok(result) => serialize_import_result(&result),
196 Err(err) => Err(import_error_to_js(err)),
197 }
198 }
199
200 #[wasm_bindgen]
210 pub fn import_from_protobuf(protobuf_content: &str) -> Result<String, JsValue> {
211 let importer = crate::import::ProtobufImporter::new();
212 match importer.import(protobuf_content) {
213 Ok(result) => serialize_import_result(&result),
214 Err(err) => Err(import_error_to_js(err)),
215 }
216 }
217
218 #[wasm_bindgen]
229 pub fn export_to_sql(workspace_json: &str, dialect: &str) -> Result<String, JsValue> {
230 let model = deserialize_workspace(workspace_json)?;
231 let exporter = crate::export::SQLExporter;
232 match exporter.export(&model.tables, Some(dialect)) {
233 Ok(result) => Ok(result.content),
234 Err(err) => Err(export_error_to_js(err)),
235 }
236 }
237
238 #[wasm_bindgen]
248 pub fn export_to_avro(workspace_json: &str) -> Result<String, JsValue> {
249 let model = deserialize_workspace(workspace_json)?;
250 let exporter = crate::export::AvroExporter;
251 match exporter.export(&model.tables) {
252 Ok(result) => Ok(result.content),
253 Err(err) => Err(export_error_to_js(err)),
254 }
255 }
256
257 #[wasm_bindgen]
267 pub fn export_to_json_schema(workspace_json: &str) -> Result<String, JsValue> {
268 let model = deserialize_workspace(workspace_json)?;
269 let exporter = crate::export::JSONSchemaExporter;
270 match exporter.export(&model.tables) {
271 Ok(result) => Ok(result.content),
272 Err(err) => Err(export_error_to_js(err)),
273 }
274 }
275
276 #[wasm_bindgen]
286 pub fn export_to_protobuf(workspace_json: &str) -> Result<String, JsValue> {
287 let model = deserialize_workspace(workspace_json)?;
288 let exporter = crate::export::ProtobufExporter;
289 match exporter.export(&model.tables) {
290 Ok(result) => Ok(result.content),
291 Err(err) => Err(export_error_to_js(err)),
292 }
293 }
294
295 #[wasm_bindgen]
305 pub fn import_from_cads(yaml_content: &str) -> Result<String, JsValue> {
306 let importer = crate::import::CADSImporter::new();
307 match importer.import(yaml_content) {
308 Ok(asset) => serde_json::to_string(&asset)
309 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
310 Err(err) => Err(import_error_to_js(err)),
311 }
312 }
313
314 #[wasm_bindgen]
324 pub fn export_to_cads(asset_json: &str) -> Result<String, JsValue> {
325 let asset: crate::models::cads::CADSAsset = serde_json::from_str(asset_json)
326 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
327 let exporter = crate::export::CADSExporter;
328 match exporter.export(&asset) {
329 Ok(yaml) => Ok(yaml),
330 Err(err) => Err(export_error_to_js(err)),
331 }
332 }
333
334 #[wasm_bindgen]
344 pub fn import_from_odps(yaml_content: &str) -> Result<String, JsValue> {
345 let importer = crate::import::ODPSImporter::new();
346 match importer.import(yaml_content) {
347 Ok(product) => serde_json::to_string(&product)
348 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
349 Err(err) => Err(import_error_to_js(err)),
350 }
351 }
352
353 #[wasm_bindgen]
363 pub fn export_to_odps(product_json: &str) -> Result<String, JsValue> {
364 let product: crate::models::odps::ODPSDataProduct = serde_json::from_str(product_json)
365 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
366 let exporter = crate::export::ODPSExporter;
367 match exporter.export(&product) {
368 Ok(yaml) => Ok(yaml),
369 Err(err) => Err(export_error_to_js(err)),
370 }
371 }
372
373 #[wasm_bindgen]
383 pub fn create_domain(name: &str) -> Result<String, JsValue> {
384 let domain = crate::models::domain::Domain::new(name.to_string());
385 serde_json::to_string(&domain)
386 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
387 }
388
389 #[wasm_bindgen]
399 pub fn import_from_domain(yaml_content: &str) -> Result<String, JsValue> {
400 match crate::models::domain::Domain::from_yaml(yaml_content) {
401 Ok(domain) => serde_json::to_string(&domain)
402 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
403 Err(e) => Err(JsValue::from_str(&format!("Parse error: {}", e))),
404 }
405 }
406
407 #[wasm_bindgen]
417 pub fn export_to_domain(domain_json: &str) -> Result<String, JsValue> {
418 let domain: crate::models::domain::Domain = serde_json::from_str(domain_json)
419 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
420 domain
421 .to_yaml()
422 .map_err(|e| JsValue::from_str(&format!("YAML serialization error: {}", e)))
423 }
424
425 #[wasm_bindgen]
436 pub fn migrate_dataflow_to_domain(
437 dataflow_yaml: &str,
438 domain_name: Option<String>,
439 ) -> Result<String, JsValue> {
440 match crate::convert::migrate_dataflow::migrate_dataflow_to_domain(
441 dataflow_yaml,
442 domain_name.as_deref(),
443 ) {
444 Ok(domain) => serde_json::to_string(&domain)
445 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
446 Err(e) => Err(JsValue::from_str(&format!("Migration error: {}", e))),
447 }
448 }
449
450 #[wasm_bindgen]
460 pub fn parse_tag(tag_str: &str) -> Result<String, JsValue> {
461 use crate::models::Tag;
462 use std::str::FromStr;
463 match Tag::from_str(tag_str) {
464 Ok(tag) => serde_json::to_string(&tag)
465 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
466 Err(_) => Err(JsValue::from_str("Invalid tag format")),
467 }
468 }
469
470 #[wasm_bindgen]
480 pub fn serialize_tag(tag_json: &str) -> Result<String, JsValue> {
481 use crate::models::Tag;
482 let tag: Tag = serde_json::from_str(tag_json)
483 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
484 Ok(tag.to_string())
485 }
486
487 #[wasm_bindgen]
499 pub fn convert_to_odcs(input: &str, format: Option<String>) -> Result<String, JsValue> {
500 match crate::convert::convert_to_odcs(input, format.as_deref()) {
501 Ok(yaml) => Ok(yaml),
502 Err(e) => Err(JsValue::from_str(&format!("Conversion error: {}", e))),
503 }
504 }
505
506 #[wasm_bindgen]
517 pub fn filter_nodes_by_owner(workspace_json: &str, owner: &str) -> Result<String, JsValue> {
518 let model = deserialize_workspace(workspace_json)?;
519 let filtered = model.filter_nodes_by_owner(owner);
520 serde_json::to_string(&filtered)
521 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
522 }
523
524 #[wasm_bindgen]
535 pub fn filter_relationships_by_owner(
536 workspace_json: &str,
537 owner: &str,
538 ) -> Result<String, JsValue> {
539 let model = deserialize_workspace(workspace_json)?;
540 let filtered = model.filter_relationships_by_owner(owner);
541 serde_json::to_string(&filtered)
542 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
543 }
544
545 #[wasm_bindgen]
556 pub fn filter_nodes_by_infrastructure_type(
557 workspace_json: &str,
558 infrastructure_type: &str,
559 ) -> Result<String, JsValue> {
560 let model = deserialize_workspace(workspace_json)?;
561 let infra_type: crate::models::enums::InfrastructureType =
562 serde_json::from_str(&format!("\"{}\"", infrastructure_type)).map_err(|e| {
563 JsValue::from_str(&format!(
564 "Invalid infrastructure type '{}': {}",
565 infrastructure_type, e
566 ))
567 })?;
568 let filtered = model.filter_nodes_by_infrastructure_type(infra_type);
569 serde_json::to_string(&filtered)
570 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
571 }
572
573 #[wasm_bindgen]
584 pub fn filter_relationships_by_infrastructure_type(
585 workspace_json: &str,
586 infrastructure_type: &str,
587 ) -> Result<String, JsValue> {
588 let model = deserialize_workspace(workspace_json)?;
589 let infra_type: crate::models::enums::InfrastructureType =
590 serde_json::from_str(&format!("\"{}\"", infrastructure_type)).map_err(|e| {
591 JsValue::from_str(&format!(
592 "Invalid infrastructure type '{}': {}",
593 infrastructure_type, e
594 ))
595 })?;
596 let filtered = model.filter_relationships_by_infrastructure_type(infra_type);
597 serde_json::to_string(&filtered)
598 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
599 }
600
601 #[wasm_bindgen]
612 pub fn filter_by_tags(workspace_json: &str, tag: &str) -> Result<String, JsValue> {
613 let model = deserialize_workspace(workspace_json)?;
614 let (nodes, relationships) = model.filter_by_tags(tag);
615 let result = serde_json::json!({
616 "nodes": nodes,
617 "relationships": relationships
618 });
619 serde_json::to_string(&result)
620 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
621 }
622
623 #[wasm_bindgen]
639 pub fn add_system_to_domain(
640 workspace_json: &str,
641 domain_id: &str,
642 system_json: &str,
643 ) -> Result<String, JsValue> {
644 let mut model = deserialize_workspace(workspace_json)?;
645 let domain_uuid = uuid::Uuid::parse_str(domain_id)
646 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
647 let system: crate::models::domain::System = serde_json::from_str(system_json)
648 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
649 model
650 .add_system_to_domain(domain_uuid, system)
651 .map_err(|e| JsValue::from_str(&e))?;
652 serde_json::to_string(&model)
653 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
654 }
655
656 #[wasm_bindgen]
668 pub fn add_cads_node_to_domain(
669 workspace_json: &str,
670 domain_id: &str,
671 node_json: &str,
672 ) -> Result<String, JsValue> {
673 let mut model = deserialize_workspace(workspace_json)?;
674 let domain_uuid = uuid::Uuid::parse_str(domain_id)
675 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
676 let node: crate::models::domain::CADSNode = serde_json::from_str(node_json)
677 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
678 model
679 .add_cads_node_to_domain(domain_uuid, node)
680 .map_err(|e| JsValue::from_str(&e))?;
681 serde_json::to_string(&model)
682 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
683 }
684
685 #[wasm_bindgen]
697 pub fn add_odcs_node_to_domain(
698 workspace_json: &str,
699 domain_id: &str,
700 node_json: &str,
701 ) -> Result<String, JsValue> {
702 let mut model = deserialize_workspace(workspace_json)?;
703 let domain_uuid = uuid::Uuid::parse_str(domain_id)
704 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
705 let node: crate::models::domain::ODCSNode = serde_json::from_str(node_json)
706 .map_err(|e| JsValue::from_str(&format!("Deserialization error: {}", e)))?;
707 model
708 .add_odcs_node_to_domain(domain_uuid, node)
709 .map_err(|e| JsValue::from_str(&e))?;
710 serde_json::to_string(&model)
711 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
712 }
713
714 #[wasm_bindgen]
728 pub fn validate_table_name(name: &str) -> Result<String, JsValue> {
729 match crate::validation::input::validate_table_name(name) {
730 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
731 Err(err) => {
732 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
733 }
734 }
735 }
736
737 #[wasm_bindgen]
747 pub fn validate_column_name(name: &str) -> Result<String, JsValue> {
748 match crate::validation::input::validate_column_name(name) {
749 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
750 Err(err) => {
751 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
752 }
753 }
754 }
755
756 #[wasm_bindgen]
766 pub fn validate_uuid(id: &str) -> Result<String, JsValue> {
767 match crate::validation::input::validate_uuid(id) {
768 Ok(uuid) => {
769 Ok(serde_json::json!({"valid": true, "uuid": uuid.to_string()}).to_string())
770 }
771 Err(err) => {
772 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
773 }
774 }
775 }
776
777 #[wasm_bindgen]
787 pub fn validate_data_type(data_type: &str) -> Result<String, JsValue> {
788 match crate::validation::input::validate_data_type(data_type) {
789 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
790 Err(err) => {
791 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
792 }
793 }
794 }
795
796 #[wasm_bindgen]
806 pub fn validate_description(desc: &str) -> Result<String, JsValue> {
807 match crate::validation::input::validate_description(desc) {
808 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
809 Err(err) => {
810 Ok(serde_json::json!({"valid": false, "error": err.to_string()}).to_string())
811 }
812 }
813 }
814
815 #[wasm_bindgen]
826 pub fn sanitize_sql_identifier(name: &str, dialect: &str) -> String {
827 crate::validation::input::sanitize_sql_identifier(name, dialect)
828 }
829
830 #[wasm_bindgen]
840 pub fn sanitize_description(desc: &str) -> String {
841 crate::validation::input::sanitize_description(desc)
842 }
843
844 #[wasm_bindgen]
855 pub fn detect_naming_conflicts(
856 existing_tables_json: &str,
857 new_tables_json: &str,
858 ) -> Result<String, JsValue> {
859 let existing_tables: Vec<crate::models::Table> = serde_json::from_str(existing_tables_json)
860 .map_err(|e| JsValue::from_str(&format!("Failed to parse existing tables: {}", e)))?;
861 let new_tables: Vec<crate::models::Table> = serde_json::from_str(new_tables_json)
862 .map_err(|e| JsValue::from_str(&format!("Failed to parse new tables: {}", e)))?;
863
864 let validator = crate::validation::tables::TableValidator::new();
865 let conflicts = validator.detect_naming_conflicts(&existing_tables, &new_tables);
866
867 serde_json::to_string(&conflicts)
868 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e)))
869 }
870
871 #[wasm_bindgen]
881 pub fn validate_pattern_exclusivity(table_json: &str) -> Result<String, JsValue> {
882 let table: crate::models::Table = serde_json::from_str(table_json)
883 .map_err(|e| JsValue::from_str(&format!("Failed to parse table: {}", e)))?;
884
885 let validator = crate::validation::tables::TableValidator::new();
886 match validator.validate_pattern_exclusivity(&table) {
887 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
888 Err(violation) => {
889 Ok(serde_json::json!({"valid": false, "violation": violation}).to_string())
890 }
891 }
892 }
893
894 #[wasm_bindgen]
906 pub fn check_circular_dependency(
907 relationships_json: &str,
908 source_table_id: &str,
909 target_table_id: &str,
910 ) -> Result<String, JsValue> {
911 let relationships: Vec<crate::models::Relationship> =
912 serde_json::from_str(relationships_json)
913 .map_err(|e| JsValue::from_str(&format!("Failed to parse relationships: {}", e)))?;
914
915 let source_id = uuid::Uuid::parse_str(source_table_id)
916 .map_err(|e| JsValue::from_str(&format!("Invalid source_table_id: {}", e)))?;
917 let target_id = uuid::Uuid::parse_str(target_table_id)
918 .map_err(|e| JsValue::from_str(&format!("Invalid target_table_id: {}", e)))?;
919
920 let validator = crate::validation::relationships::RelationshipValidator::new();
921 match validator.check_circular_dependency(&relationships, source_id, target_id) {
922 Ok((has_cycle, cycle_path)) => {
923 let cycle_path_strs: Vec<String> = cycle_path
924 .map(|path| path.iter().map(|id| id.to_string()).collect())
925 .unwrap_or_default();
926 Ok(serde_json::json!({
927 "has_cycle": has_cycle,
928 "cycle_path": cycle_path_strs
929 })
930 .to_string())
931 }
932 Err(err) => Err(JsValue::from_str(&format!("Validation error: {}", err))),
933 }
934 }
935
936 #[wasm_bindgen]
947 pub fn validate_no_self_reference(
948 source_table_id: &str,
949 target_table_id: &str,
950 ) -> Result<String, JsValue> {
951 let source_id = uuid::Uuid::parse_str(source_table_id)
952 .map_err(|e| JsValue::from_str(&format!("Invalid source_table_id: {}", e)))?;
953 let target_id = uuid::Uuid::parse_str(target_table_id)
954 .map_err(|e| JsValue::from_str(&format!("Invalid target_table_id: {}", e)))?;
955
956 let validator = crate::validation::relationships::RelationshipValidator::new();
957 match validator.validate_no_self_reference(source_id, target_id) {
958 Ok(()) => Ok(serde_json::json!({"valid": true}).to_string()),
959 Err(self_ref) => {
960 Ok(serde_json::json!({"valid": false, "self_reference": self_ref}).to_string())
961 }
962 }
963 }
964
965 #[cfg(feature = "png-export")]
981 #[wasm_bindgen]
982 pub fn export_to_png(workspace_json: &str, width: u32, height: u32) -> Result<String, JsValue> {
983 let model = deserialize_workspace(workspace_json)?;
984 let exporter = crate::export::PNGExporter::new();
985 match exporter.export(&model.tables, width, height) {
986 Ok(result) => Ok(result.content), Err(err) => Err(export_error_to_js(err)),
988 }
989 }
990
991 #[wasm_bindgen]
1007 pub fn load_model(db_name: &str, store_name: &str, workspace_path: &str) -> js_sys::Promise {
1008 let db_name = db_name.to_string();
1009 let store_name = store_name.to_string();
1010 let workspace_path = workspace_path.to_string();
1011
1012 wasm_bindgen_futures::future_to_promise(async move {
1013 let storage = crate::storage::browser::BrowserStorageBackend::new(db_name, store_name);
1014 let loader = crate::model::ModelLoader::new(storage);
1015 match loader.load_model(&workspace_path).await {
1016 Ok(result) => serde_json::to_string(&result)
1017 .map(|s| JsValue::from_str(&s))
1018 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1019 Err(err) => Err(JsValue::from_str(&format!("Storage error: {}", err))),
1020 }
1021 })
1022 }
1023
1024 #[wasm_bindgen]
1037 pub fn save_model(
1038 db_name: &str,
1039 store_name: &str,
1040 workspace_path: &str,
1041 model_json: &str,
1042 ) -> js_sys::Promise {
1043 let db_name = db_name.to_string();
1044 let store_name = store_name.to_string();
1045 let workspace_path = workspace_path.to_string();
1046 let model_json = model_json.to_string();
1047
1048 wasm_bindgen_futures::future_to_promise(async move {
1049 let model: crate::models::DataModel = serde_json::from_str(&model_json)
1050 .map_err(|e| JsValue::from_str(&format!("Failed to parse model: {}", e)))?;
1051
1052 let storage = crate::storage::browser::BrowserStorageBackend::new(db_name, store_name);
1053 let saver = crate::model::ModelSaver::new(storage);
1054
1055 for table in &model.tables {
1058 let yaml = crate::export::ODCSExporter::export_table(table, "odcs_v3_1_0");
1060 let table_data = crate::model::saver::TableData {
1061 id: table.id,
1062 name: table.name.clone(),
1063 yaml_file_path: Some(format!("tables/{}.yaml", table.name)),
1064 yaml_value: serde_yaml::from_str(&yaml)
1065 .map_err(|e| JsValue::from_str(&format!("Failed to parse YAML: {}", e)))?,
1066 };
1067 saver
1068 .save_table(&workspace_path, &table_data)
1069 .await
1070 .map_err(|e| JsValue::from_str(&format!("Failed to save table: {}", e)))?;
1071 }
1072
1073 if !model.relationships.is_empty() {
1075 let rel_data: Vec<crate::model::saver::RelationshipData> = model
1076 .relationships
1077 .iter()
1078 .map(|rel| {
1079 let yaml_value = serde_json::json!({
1080 "id": rel.id.to_string(),
1081 "source_table_id": rel.source_table_id.to_string(),
1082 "target_table_id": rel.target_table_id.to_string(),
1083 });
1084 let yaml_str = serde_json::to_string(&yaml_value)
1086 .map_err(|e| format!("Failed to serialize relationship: {}", e))?;
1087 let yaml_value = serde_yaml::from_str(&yaml_str)
1088 .map_err(|e| format!("Failed to convert to YAML: {}", e))?;
1089 Ok(crate::model::saver::RelationshipData {
1090 id: rel.id,
1091 source_table_id: rel.source_table_id,
1092 target_table_id: rel.target_table_id,
1093 yaml_value,
1094 })
1095 })
1096 .collect::<Result<Vec<_>, String>>()
1097 .map_err(|e| JsValue::from_str(&e))?;
1098
1099 saver
1100 .save_relationships(&workspace_path, &rel_data)
1101 .await
1102 .map_err(|e| {
1103 JsValue::from_str(&format!("Failed to save relationships: {}", e))
1104 })?;
1105 }
1106
1107 Ok(JsValue::from_str("Model saved successfully"))
1108 })
1109 }
1110
1111 #[cfg(feature = "bpmn")]
1124 #[wasm_bindgen]
1125 pub fn import_bpmn_model(
1126 domain_id: &str,
1127 xml_content: &str,
1128 model_name: Option<String>,
1129 ) -> Result<String, JsValue> {
1130 use crate::import::bpmn::BPMNImporter;
1131 use uuid::Uuid;
1132
1133 let domain_uuid = Uuid::parse_str(domain_id)
1134 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1135
1136 let mut importer = BPMNImporter::new();
1137 match importer.import(xml_content, domain_uuid, model_name.as_deref()) {
1138 Ok(model) => serde_json::to_string(&model)
1139 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1140 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1141 }
1142 }
1143
1144 #[cfg(feature = "bpmn")]
1154 #[wasm_bindgen]
1155 pub fn export_bpmn_model(xml_content: &str) -> Result<String, JsValue> {
1156 use crate::export::bpmn::BPMNExporter;
1157 let exporter = BPMNExporter::new();
1158 exporter
1159 .export(xml_content)
1160 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1161 }
1162
1163 #[cfg(feature = "dmn")]
1176 #[wasm_bindgen]
1177 pub fn import_dmn_model(
1178 domain_id: &str,
1179 xml_content: &str,
1180 model_name: Option<String>,
1181 ) -> Result<String, JsValue> {
1182 use crate::import::dmn::DMNImporter;
1183 use uuid::Uuid;
1184
1185 let domain_uuid = Uuid::parse_str(domain_id)
1186 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1187
1188 let mut importer = DMNImporter::new();
1189 match importer.import(xml_content, domain_uuid, model_name.as_deref()) {
1190 Ok(model) => serde_json::to_string(&model)
1191 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1192 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1193 }
1194 }
1195
1196 #[cfg(feature = "dmn")]
1206 #[wasm_bindgen]
1207 pub fn export_dmn_model(xml_content: &str) -> Result<String, JsValue> {
1208 use crate::export::dmn::DMNExporter;
1209 let exporter = DMNExporter::new();
1210 exporter
1211 .export(xml_content)
1212 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1213 }
1214
1215 #[cfg(feature = "openapi")]
1228 #[wasm_bindgen]
1229 pub fn import_openapi_spec(
1230 domain_id: &str,
1231 content: &str,
1232 api_name: Option<String>,
1233 ) -> Result<String, JsValue> {
1234 use crate::import::openapi::OpenAPIImporter;
1235 use uuid::Uuid;
1236
1237 let domain_uuid = Uuid::parse_str(domain_id)
1238 .map_err(|e| JsValue::from_str(&format!("Invalid domain ID: {}", e)))?;
1239
1240 let mut importer = OpenAPIImporter::new();
1241 match importer.import(content, domain_uuid, api_name.as_deref()) {
1242 Ok(model) => serde_json::to_string(&model)
1243 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1244 Err(e) => Err(JsValue::from_str(&format!("Import error: {}", e))),
1245 }
1246 }
1247
1248 #[cfg(feature = "openapi")]
1260 #[wasm_bindgen]
1261 pub fn export_openapi_spec(
1262 content: &str,
1263 source_format: &str,
1264 target_format: Option<String>,
1265 ) -> Result<String, JsValue> {
1266 use crate::export::openapi::OpenAPIExporter;
1267 use crate::models::openapi::OpenAPIFormat;
1268
1269 let source_fmt = match source_format {
1270 "yaml" | "yml" => OpenAPIFormat::Yaml,
1271 "json" => OpenAPIFormat::Json,
1272 _ => {
1273 return Err(JsValue::from_str(
1274 "Invalid source format. Use 'yaml' or 'json'",
1275 ));
1276 }
1277 };
1278
1279 let target_fmt = if let Some(tf) = target_format {
1280 match tf.as_str() {
1281 "yaml" | "yml" => Some(OpenAPIFormat::Yaml),
1282 "json" => Some(OpenAPIFormat::Json),
1283 _ => {
1284 return Err(JsValue::from_str(
1285 "Invalid target format. Use 'yaml' or 'json'",
1286 ));
1287 }
1288 }
1289 } else {
1290 None
1291 };
1292
1293 let exporter = OpenAPIExporter::new();
1294 exporter
1295 .export(content, source_fmt, target_fmt)
1296 .map_err(|e| JsValue::from_str(&format!("Export error: {}", e)))
1297 }
1298
1299 #[cfg(feature = "openapi")]
1311 #[wasm_bindgen]
1312 pub fn convert_openapi_to_odcs(
1313 openapi_content: &str,
1314 component_name: &str,
1315 table_name: Option<String>,
1316 ) -> Result<String, JsValue> {
1317 use crate::convert::openapi_to_odcs::OpenAPIToODCSConverter;
1318
1319 let converter = OpenAPIToODCSConverter::new();
1320 match converter.convert_component(openapi_content, component_name, table_name.as_deref()) {
1321 Ok(table) => serde_json::to_string(&table)
1322 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1323 Err(e) => Err(JsValue::from_str(&format!("Conversion error: {}", e))),
1324 }
1325 }
1326
1327 #[cfg(feature = "openapi")]
1338 #[wasm_bindgen]
1339 pub fn analyze_openapi_conversion(
1340 openapi_content: &str,
1341 component_name: &str,
1342 ) -> Result<String, JsValue> {
1343 use crate::convert::openapi_to_odcs::OpenAPIToODCSConverter;
1344
1345 let converter = OpenAPIToODCSConverter::new();
1346 match converter.analyze_conversion(openapi_content, component_name) {
1347 Ok(report) => serde_json::to_string(&report)
1348 .map_err(|e| JsValue::from_str(&format!("Serialization error: {}", e))),
1349 Err(e) => Err(JsValue::from_str(&format!("Analysis error: {}", e))),
1350 }
1351 }
1352}