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