1
0

parse magic draw project files

This commit is contained in:
Rokas Puzonas 2023-02-26 11:52:41 +02:00
commit ef87fa39c3
12 changed files with 2104 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
dist

986
Cargo.lock generated Normal file
View File

@ -0,0 +1,986 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "anymap2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "boolinator"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "form_urlencoded"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
[[package]]
name = "futures-io"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
[[package]]
name = "futures-macro"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
[[package]]
name = "futures-task"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
[[package]]
name = "futures-util"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gloo"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20"
dependencies = [
"gloo-console",
"gloo-dialogs",
"gloo-events",
"gloo-file",
"gloo-history",
"gloo-net",
"gloo-render",
"gloo-storage",
"gloo-timers",
"gloo-utils",
"gloo-worker",
]
[[package]]
name = "gloo-console"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
dependencies = [
"gloo-utils",
"js-sys",
"serde",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-dialogs"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-events"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-file"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
dependencies = [
"gloo-events",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-history"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7"
dependencies = [
"gloo-events",
"gloo-utils",
"serde",
"serde-wasm-bindgen",
"serde_urlencoded",
"thiserror",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-net"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10"
dependencies = [
"futures-channel",
"futures-core",
"futures-sink",
"gloo-utils",
"js-sys",
"pin-project",
"serde",
"serde_json",
"thiserror",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "gloo-render"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
dependencies = [
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-storage"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
dependencies = [
"gloo-utils",
"js-sys",
"serde",
"serde_json",
"thiserror",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "gloo-utils"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5"
dependencies = [
"js-sys",
"serde",
"serde_json",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "gloo-worker"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
dependencies = [
"anymap2",
"bincode",
"gloo-console",
"gloo-utils",
"js-sys",
"serde",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "implicit-clone"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7"
dependencies = [
"indexmap",
]
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy-regex"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a505da2f89befd87ab425d252795f0f285e100b43e7d22d29528df3d9a576793"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8edfc11b8f56ce85e207e62ea21557cfa09bb24a8f6b04ae181b086ff8611c22"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "libc"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "magic-sql-gen"
version = "0.1.0"
dependencies = [
"anyhow",
"lazy-regex",
"thiserror",
"xml-rs",
"yew",
"zip",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "percent-encoding"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "pin-project"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pinned"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
dependencies = [
"futures",
"rustversion",
"thiserror",
]
[[package]]
name = "prettyplease"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "prokio"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
dependencies = [
"futures",
"gloo",
"num_cpus",
"once_cell",
"pin-project",
"pinned",
"tokio",
"tokio-stream",
"wasm-bindgen-futures",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "rustversion"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
[[package]]
name = "ryu"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "serde"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-wasm-bindgen"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
dependencies = [
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "serde_derive"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "slab"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg",
"pin-project-lite",
"windows-sys",
]
[[package]]
name = "tokio-stream"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
[[package]]
name = "yew"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
dependencies = [
"console_error_panic_hook",
"futures",
"gloo",
"implicit-clone",
"indexmap",
"js-sys",
"prokio",
"rustversion",
"serde",
"slab",
"thiserror",
"tokio",
"tracing",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"yew-macro",
]
[[package]]
name = "yew-macro"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
dependencies = [
"boolinator",
"once_cell",
"prettyplease",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zip"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef"
dependencies = [
"byteorder",
"crc32fast",
"crossbeam-utils",
"flate2",
]

17
Cargo.toml Normal file
View File

@ -0,0 +1,17 @@
[package]
name = "magic-sql-gen"
version = "0.1.0"
edition = "2021"
license = "MIT"
[dependencies]
xml-rs = "0.8.4"
yew = { version="0.20", features=["csr"] }
anyhow = "1.0.69"
thiserror = "1.0.38"
lazy-regex = "2.4.1"
[dependencies.zip]
version = "0.6.4"
default-features = false
features=["deflate"]

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright © 2023 Rokas Puzonas
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

8
index.html Normal file
View File

@ -0,0 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Trunk Template</title>
<link data-trunk rel="sass" href="index.scss" />
</head>
</html>

35
index.scss Normal file
View File

@ -0,0 +1,35 @@
html,
body {
height: 100%;
margin: 0;
}
body {
align-items: center;
display: flex;
justify-content: center;
background: linear-gradient(to bottom right, #444444, #009a5b);
font-size: 1.5rem;
}
main {
color: #fff6d5;
font-family: sans-serif;
text-align: center;
}
.logo {
height: 20em;
}
.heart:after {
content: "❤️";
font-size: 1.75em;
}
h1 + .subtitle {
display: block;
margin-top: -1em;
}

View File

@ -0,0 +1,141 @@
use std::io::{Read, Seek};
use xml::attribute::OwnedAttribute;
use xml::name::OwnedName;
use xml::{EventReader, reader::XmlEvent};
use zip::ZipArchive;
use anyhow::{Result, Context, Ok};
use crate::magicdraw_parser::utils::get_attribute;
use super::utils::{check_name, check_attribute, MyEventReader, parse_element};
#[derive(Debug)]
pub struct DDLClass {
pub class_id: String,
pub property_ids: Vec<String>
}
#[derive(Debug)]
pub struct DDLScript {
pub script_id: String,
pub classess: Vec<DDLClass>
}
#[derive(Debug)]
pub struct DDLProject {
pub model_id: String,
pub scripts: Vec<DDLScript>
}
fn get_id_from_href(attrs: &[OwnedAttribute]) -> Option<String> {
let href = get_attribute(attrs, None, "href").ok()?;
let parts = href.split_once("#")?;
Some(parts.1.to_string())
}
fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLClass> {
let mut property_ids = vec![];
let mut class_id = None;
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Class")
}
fn is_property_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Property")
}
parse_element(parser, &mut |p, name, attrs| {
if is_model_element(&name, &attrs) && class_id.is_none() {
class_id = get_id_from_href(&attrs);
} else if is_property_element(&name, &attrs) {
property_ids.push(get_id_from_href(&attrs).context("Property id not found")?);
}
Ok(())
})?;
Ok(DDLClass {
class_id: class_id.context("Missing class id")?,
property_ids
})
}
fn parse_script<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLScript> {
let mut classess = vec![];
let mut script_id = None;
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Component")
}
fn is_class_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "objects") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.rt.objects:RTClassObject")
}
parse_element(parser, &mut |p, name, attrs| {
if is_model_element(&name, &attrs) && script_id.is_none() {
script_id = get_id_from_href(&attrs);
} else if is_class_element(&name, &attrs) {
classess.push(parse_class(p, &attrs)?);
}
Ok(())
})?;
Ok(DDLScript {
script_id: script_id.context("Missing script id")?,
classess
})
}
fn parse_project<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<DDLProject> {
let mut scripts = vec![];
let mut model_id = None;
fn is_model_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "modelElement") && check_attribute(&attributes, Some("xsi"), "type", "uml:Model")
}
fn is_component_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "objects") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.rt.objects:RTComponent")
}
parse_element(parser, &mut |p, name, attrs| {
if is_model_element(&name, &attrs) && model_id.is_none() {
model_id = get_id_from_href(&attrs);
} else if is_component_element(&name, &attrs) {
scripts.push(parse_script(p, &attrs)?);
}
Ok(())
})?;
Ok(DDLProject {
model_id: model_id.context("Missing model id")?,
scripts
})
}
pub fn parse_ddl_scripts<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<Vec<DDLProject>> {
let mut ddl_scripts = vec![];
let file = project.by_name("personal-com.nomagic.magicdraw.ce.dmn.personaldmncodeengineering")?;
let mut parser: MyEventReader<_> = EventReader::new(file).into();
fn is_project_element(name: &OwnedName, attributes: &[OwnedAttribute]) -> bool {
check_name(name, None, "contents") && check_attribute(&attributes, Some("xsi"), "type", "md.ce.ddl.rt.objects:DDLProjectObject")
}
loop {
match parser.next()? {
XmlEvent::StartElement { name, attributes, .. } => {
if is_project_element(&name, &attributes) {
ddl_scripts.push(parse_project(&mut parser, &attributes)?);
}
},
XmlEvent::EndDocument => { break; },
_ => {}
}
}
Ok(ddl_scripts)
}

263
src/magicdraw_parser/mod.rs Normal file
View File

@ -0,0 +1,263 @@
#![allow(dead_code)]
#![allow(unused_variables)]
mod utils;
mod uml_model_parser;
mod ddl_parser;
mod sql_types_parser;
use std::{io::{Read, Seek}, collections::HashSet};
use anyhow::{Result, Context};
use lazy_regex::regex_captures;
use zip::ZipArchive;
use crate::unwrap_opt_continue;
use self::{uml_model_parser::{parse_uml_model, UMLModel, UMLClass, UMLModifier, UMLNullableModifier, UMLPrimaryKeyModifier, UMLTypeModifier, UMLForeignKeyModifier}, ddl_parser::parse_ddl_scripts, sql_types_parser::{parse_sql_types, SQLTypeName}};
#[derive(Debug)]
pub enum SQLType {
Int,
Decimal,
Date,
Float,
Bool,
Char(u8),
Varchar(u16),
}
#[derive(Debug)]
pub enum SQLCheckConstraint {
OneOf(Vec<String>),
Freeform(String)
}
#[derive(Debug)]
pub struct SQLColumn {
name: String,
sql_type: SQLType,
primary_key: bool,
nullable: bool,
foreign_key: Option<(String, String)>,
check_constraint: Option<SQLCheckConstraint>
}
#[derive(Debug)]
pub struct SQLTable {
name: String,
columns: Vec<SQLColumn>,
}
#[derive(Debug)]
pub struct SQLTableCollection {
tables: Vec<SQLTable>
}
fn find_class_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLClass> {
for model in models {
for package in &model.packages {
if let Some(class) = package.classess.iter().find(|t| t.id.eq(id)) {
return Some(class);
}
}
}
None
}
fn is_nullabe(modifiers: &[UMLModifier], property: &str) -> bool {
for modifier in modifiers {
if let UMLModifier::Nullable(UMLNullableModifier { property_id, nullable }) = modifier {
if property_id.eq(property) {
return *nullable;
}
}
}
false
}
fn is_primary_key(modifiers: &[UMLModifier], property: &str) -> bool {
for modifier in modifiers {
if let UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }) = modifier {
if property_id.eq(property) {
return true
}
}
}
false
}
fn get_type_modifier<'a>(modifiers: &'a [UMLModifier], property: &str) -> Option<&'a str> {
for modifier in modifiers {
if let UMLModifier::Type(UMLTypeModifier { property_id, modifier }) = modifier {
if property_id.eq(property) {
return Some(modifier)
}
}
}
None
}
fn get_foreign_key_constraint<'a>(modifiers: &'a [UMLModifier], from_id: &str) -> Option<&'a str> {
for modifier in modifiers {
if let UMLModifier::ForeignKey(UMLForeignKeyModifier { from_property_id, to_property_id }) = modifier {
if from_property_id.eq(from_id) {
return Some(&to_property_id)
}
}
}
None
}
fn get_foreign_key(modifiers: &[UMLModifier], classess: &[&UMLClass], property: &str) -> Result<Option<(String, String)>> {
let to_id = get_foreign_key_constraint(modifiers, property);
if to_id.is_none() {
return Ok(None)
}
let to_id = to_id.unwrap();
for class in classess {
for property in &class.properties {
if property.id.eq(to_id) {
let property_name = property.name.clone().context("Missing property name")?;
let class_name = class.name.clone().context("Missing class name")?;
return Ok(Some((class_name, property_name)));
}
}
}
Ok(None)
}
fn parse_check_constraint(str: &str) -> SQLCheckConstraint {
fn try_parse_one_of(str: &str) -> Option<SQLCheckConstraint> {
let (_, inner) = regex_captures!(r#"^in \((.+)\)$"#, str)?;
let mut variants = vec![];
for part in inner.split(", ") {
let (_, variant) = regex_captures!(r#"^'(.+)'$"#, part)?;
variants.push(variant.to_string());
}
Some(SQLCheckConstraint::OneOf(variants))
}
try_parse_one_of(str)
.unwrap_or(SQLCheckConstraint::Freeform(str.to_string()))
}
// TODO: Refactor this function, less nesting would be good
fn get_sql_check_constraint<'a>(models: &'a [UMLModel], property_name: &str) -> Option<SQLCheckConstraint> {
for model in models {
for package in &model.packages {
for class in &package.classess {
for constraint in &class.constraints {
let prop_name = unwrap_opt_continue!(&constraint.property_name);
let body = unwrap_opt_continue!(&constraint.body);
if prop_name.eq(property_name) && constraint.body.is_some() {
return Some(parse_check_constraint(body));
}
}
}
}
}
None
}
fn get_sql_type(modifiers: &[UMLModifier], type_name: SQLTypeName, property: &str) -> Result<SQLType> {
Ok(match type_name {
SQLTypeName::Int => SQLType::Int,
SQLTypeName::Date => SQLType::Date,
SQLTypeName::Float => SQLType::Float,
SQLTypeName::Bool => SQLType::Bool,
SQLTypeName::Decimal => SQLType::Decimal,
SQLTypeName::Char => {
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
.context("Type modifier doesn't match format")?;
SQLType::Char(size.parse()?)
} else {
// TODO: Add better error message to say which table is missing type modifier
// For now just pick a defautl arbitrarily
SQLType::Char(31)
}
},
SQLTypeName::Varchar => {
if let Some(type_modifier) = get_type_modifier(modifiers, property) {
let (_, size) = regex_captures!(r#"^\((\d+)\)$"#, type_modifier)
.context("Type modifier doesn't match format")?;
SQLType::Varchar(size.parse()?)
} else {
// TODO: Add better error message to say which table is missing type modifier
// For now just pick a defautl arbitrarily
SQLType::Varchar(255)
}
},
})
}
fn get_used_types<'a>(models: &'a [UMLModel]) -> HashSet<&'a String> {
models.iter()
.flat_map(|model| &model.packages)
.flat_map(|package| &package.classess)
.flat_map(|class| &class.properties)
.filter_map(|property| property.type_href.as_ref())
.collect::<HashSet<_>>()
}
pub fn parse_project<R: Read + Seek>(project_file: R) -> Result<Vec<SQLTableCollection>> {
let mut zip = ZipArchive::new(project_file).unwrap();
let (models, modifiers) = parse_uml_model(&mut zip)?;
let ddl_scripts = parse_ddl_scripts(&mut zip)?;
let sql_type_names = parse_sql_types(&mut zip, &get_used_types(&models))?;
let mut collections = vec![];
for ddl_project in ddl_scripts {
for ddl_script in ddl_project.scripts {
let mut tables = vec![];
let model_properties = ddl_script.classess.iter()
.flat_map(|class| class.property_ids.iter().map(|prop| (&class.class_id, prop)))
.collect::<Vec<_>>();
let mut model_classess = vec![];
for ddl_class in &ddl_script.classess {
let model_class = find_class_by_id(&models, &ddl_class.class_id).context("UML class not found")?;
model_classess.push(model_class);
}
for (ddl_class, model_class) in ddl_script.classess.iter().zip(&model_classess) {
let name = model_class.name.clone().context("UML class name not found")?;
let mut columns = vec![];
for property_id in &ddl_class.property_ids {
let property = model_class.properties.iter().find(|p| p.id.eq(property_id)).context("Property not found")?;
let prop_name = unwrap_opt_continue!(&property.name).clone();
let type_href = unwrap_opt_continue!(&property.type_href);
let type_name = sql_type_names.get(type_href).context("Proerty type name conversion not found")?;
let check_constraint = get_sql_check_constraint(&models, &prop_name);
let foreign_key = get_foreign_key(&modifiers, &model_classess, property_id)?;
columns.push(SQLColumn {
name: prop_name,
sql_type: get_sql_type(&modifiers, *type_name, property_id)?,
primary_key: is_primary_key(&modifiers, property_id),
nullable: is_nullabe(&modifiers, property_id),
foreign_key,
check_constraint,
})
}
tables.push(SQLTable {
name,
columns
})
}
collections.push(SQLTableCollection { tables })
}
}
Ok(collections)
}

View File

@ -0,0 +1,169 @@
use std::{io::{Read, Seek}, collections::{HashMap, HashSet}};
use xml::{EventReader, reader::XmlEvent, attribute::OwnedAttribute, name::OwnedName};
use zip::ZipArchive;
use anyhow::{Result, Context, bail};
use crate::unwrap_opt_continue;
use super::utils::{MyEventReader, check_name, parse_element, get_attribute, check_attribute};
#[derive(Debug)]
struct UsedPackage {
share_point_id: String,
name: String,
needed_types: Vec<String>
}
#[derive(Debug, Clone, Copy)]
pub enum SQLTypeName {
Int,
Decimal,
Date,
Float,
Bool,
Char,
Varchar
}
fn get_used_project_name(attrs: &[OwnedAttribute]) -> Option<&str> {
let project_uri = get_attribute(&attrs, None, "usedProjectURI").ok()?;
project_uri.split("/").last()
}
fn parse_used_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute], needed_types: &[&str]) -> Result<UsedPackage> {
let mut share_point_id = None;
let project_uri = get_attribute(&attrs, None, "usedProjectURI")?;
let name = project_uri.split("/").last().unwrap();
parse_element(parser, &mut |p, name, attrs| {
if share_point_id.is_none() && check_name(&name, None, "mountPoints") {
share_point_id = get_attribute(&attrs, None, "sharePointID").ok().map(str::to_string);
}
Ok(())
})?;
Ok(UsedPackage {
name: name.to_string(),
share_point_id: share_point_id.context("Share point id not found")?,
needed_types: needed_types.iter().map(|s| s.to_string()).collect()
})
}
fn list_used_packages<R: Read>(file: R, needed_types: &HashSet<&String>) -> Result<Vec<UsedPackage>> {
let mut packages = vec![];
let mut needed_types_per_package = HashMap::new();
for needed_type in needed_types.iter() {
let (package_name, type_id) = unwrap_opt_continue!(needed_type.split_once("#"));
let ids = needed_types_per_package.entry(package_name).or_insert(vec![]);
ids.push(type_id);
}
let mut parser: MyEventReader<_> = EventReader::new(file).into();
loop {
match parser.next()? {
XmlEvent::StartElement { name, attributes, .. } => {
if check_name(&name, None, "projectUsages") {
let project_name = unwrap_opt_continue!(get_used_project_name(&attributes));
if let Some(needed_types_for_package) = needed_types_per_package.get(&project_name) {
packages.push(parse_used_package(&mut parser, &attributes, needed_types_for_package)?);
}
}
},
XmlEvent::EndDocument => { break; },
_ => {}
}
}
Ok(packages)
}
fn is_umodel_snapshot_file(filename: &str) -> bool {
filename.ends_with("_resource_com$dnomagic$dmagicdraw$duml_umodel$dshared_umodel$dsnapshot")
}
fn parse_type_name(str: &str) -> Result<SQLTypeName> {
use SQLTypeName::*;
Ok(match str {
"decimal" => Decimal,
"char" => Char,
"varchar" => Varchar,
"float" => Float,
"Integer" | "integer" | "int" => Int,
"date" => Date,
"Boolean" => Bool,
_ => bail!("Unknown SQL type: '{}'", str)
})
}
fn parse_types_package<R: Read>(parser: &mut MyEventReader<R>) -> Result<Vec<(String, SQLTypeName)>> {
let mut types = vec![];
fn is_primitive_type_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
check_name(&name, None, "packagedElement") && check_attribute(&attrs, Some("xsi"), "type", "uml:PrimitiveType")
}
parse_element(parser, &mut |p, name, attrs| {
if is_primitive_type_element(&name, &attrs) {
let type_name = get_attribute(&attrs, None, "name")?;
if !type_name.eq("StructuredExpression") {
types.push((
get_attribute(&attrs, Some("xmi"), "id")?.to_string(),
parse_type_name(type_name)?
));
}
}
Ok(())
})?;
Ok(types)
}
fn parse_primitive_types<R: Read>(reader: R, used_packages: &[UsedPackage]) -> Result<Vec<(String, SQLTypeName)>> {
let mut types = vec![];
let mut parser: MyEventReader<_> = EventReader::new(reader).into();
loop {
match parser.next()? {
XmlEvent::StartElement { name, attributes, .. } => {
if check_name(&name, Some("uml"), "Package") {
if let Some(id) = get_attribute(&attributes, None, "ID").ok() {
if let Some(package) = used_packages.iter().find(|p| p.share_point_id.eq(id)) {
let package_types = parse_types_package(&mut parser)?
.into_iter()
.filter(|t| package.needed_types.contains(&t.0))
.map(|(id, type_name)| (format!("{}#{}", package.name, id), type_name));
types.extend(package_types);
}
}
}
},
XmlEvent::EndDocument => { break; },
_ => {}
}
}
Ok(types)
}
pub fn parse_sql_types<R: Read + Seek>(project: &mut ZipArchive<R>, needed_types: &HashSet<&String>) -> Result<HashMap<String, SQLTypeName>> {
let mut type_names = HashMap::new();
let meta_model_file = project.by_name("com.nomagic.ci.metamodel.project")?;
let used_packages = list_used_packages(meta_model_file, needed_types)?;
let snapshot_files = project.file_names()
.filter(|f| is_umodel_snapshot_file(f))
.map(|f| f.to_string())
.collect::<Vec<_>>();
for filename in &snapshot_files {
let f = project.by_name(filename).unwrap();
for (id, type_name) in parse_primitive_types(f, &used_packages)? {
type_names.insert(id, type_name);
}
}
Ok(type_names)
}

View File

@ -0,0 +1,294 @@
use std::io::{Read, Seek};
use xml::{EventReader, reader::XmlEvent, attribute::OwnedAttribute, name::OwnedName};
use zip::ZipArchive;
use anyhow::{Result, Context};
use crate::{unwrap_err_continue, unwrap_opt_continue};
use super::utils::{check_name, parse_element, get_attribute, check_attribute, MyEventReader, ParseProjectError, get_element_characters};
#[derive(Debug)]
pub struct UMLProperty {
pub id: String,
pub name: Option<String>,
pub is_id: bool,
pub type_href: Option<String>
}
// TODO: Make this an enum? Because from what I have seen there were only 2 cases,
// * Constraint applied to property
// * Constraint applied to class
#[derive(Debug)]
pub struct UMLConstraint {
pub id: String,
pub class_id: Option<String>,
pub property_id: Option<String>,
pub property_name: Option<String>,
pub body: Option<String>,
}
#[derive(Debug)]
pub struct UMLClass {
pub id: String,
pub name: Option<String>,
pub properties: Vec<UMLProperty>,
pub constraints: Vec<UMLConstraint>
}
#[derive(Debug)]
pub struct UMLPackage {
pub id: String,
pub name: Option<String>,
pub classess: Vec<UMLClass>
}
#[derive(Debug)]
pub struct UMLModel {
pub id: String,
pub name: String,
pub packages: Vec<UMLPackage>
}
#[derive(Debug)]
pub struct UMLPrimaryKeyModifier {
pub property_id: String
}
#[derive(Debug)]
pub struct UMLNullableModifier {
pub property_id: String,
pub nullable: bool
}
#[derive(Debug)]
pub struct UMLForeignKeyModifier {
pub from_property_id: String,
pub to_property_id: String,
}
#[derive(Debug)]
pub struct UMLUniqueModifier {
pub property_id: String
}
#[derive(Debug)]
pub struct UMLTypeModifier {
pub property_id: String,
pub modifier: String
}
#[derive(Debug)]
pub enum UMLModifier {
Unique(UMLUniqueModifier),
PirmaryKey(UMLPrimaryKeyModifier),
Nullable(UMLNullableModifier),
ForeignKey(UMLForeignKeyModifier),
Type(UMLTypeModifier)
}
fn parse_property<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLProperty> {
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
let is_id = get_attribute(attrs, None, "isID").unwrap_or("false").eq("true");
let mut type_href = None;
parse_element(parser, &mut |p, name, attrs| {
if check_name(&name, None, "type") && type_href.is_none() {
if let Ok(value) = get_attribute(&attrs, None, "href") {
type_href = Some(value.to_string());
}
}
Ok(())
})?;
Ok(UMLProperty {
id,
name,
is_id,
type_href
})
}
fn parse_constraint<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<Option<UMLConstraint>> {
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
let mut constrainted_element_id = None;
let mut language = None;
let mut body = None;
parse_element(parser, &mut |p, name, attrs| {
if check_name(&name, None, "constrainedElement") && constrainted_element_id.is_none() {
constrainted_element_id = get_attribute(&attrs, Some("xmi"), "idref").ok().map(str::to_string);
} else if check_name(&name, None, "body") && body.is_none() {
let contents = get_element_characters(p)?;
if contents.len() > 0 {
body = Some(contents);
}
} else if check_name(&name, None, "language") && language.is_none() {
language = Some(get_element_characters(p)?);
}
Ok(())
})?;
if language.eq(&Some("SQL".into())) && body.is_some() {
if let Some((prop_name, check_body)) = body.unwrap().split_once(" in ") {
return Ok(Some(UMLConstraint {
id,
class_id: Some(constrainted_element_id.context("Missing class id")?),
body: Some(format!("in {}", check_body)),
property_id: None,
property_name: Some(prop_name.into())
}))
}
}
return Ok(Some(UMLConstraint {
id,
property_id: Some(constrainted_element_id.context("Missing property id")?),
body: None,
class_id: None,
property_name: None
}))
}
fn parse_class<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLClass> {
let mut properties = vec![];
let mut consraints = vec![];
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
fn is_property_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
check_name(name, None, "ownedAttribute") && check_attribute(&attrs, Some("xmi"), "type", "uml:Property")
}
fn is_constraint_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
check_name(name, None, "ownedRule") && check_attribute(&attrs, Some("xmi"), "type", "uml:Constraint")
}
parse_element(parser, &mut |p, name, attrs| {
if is_property_element(&name, &attrs) {
properties.push(parse_property(p, &attrs)?);
} else if is_constraint_element(&name, &attrs) {
if let Some(constraint) = parse_constraint(p, &attrs)? {
consraints.push(constraint);
}
}
Ok(())
})?;
Ok(UMLClass {
id,
name,
properties,
constraints: consraints,
})
}
fn parse_package<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLPackage> {
let mut classess = vec![];
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
let name = get_attribute(attrs, None, "name").ok().map(str::to_string);
fn is_class_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
check_name(name, None, "packagedElement") && check_attribute(&attrs, Some("xmi"), "type", "uml:Class")
}
parse_element(parser, &mut |p, name, attrs| {
if is_class_element(&name, &attrs) {
classess.push(parse_class(p, &attrs)?);
}
Ok(())
})?;
Ok(UMLPackage {
id,
name,
classess
})
}
fn parse_model<R: Read>(parser: &mut MyEventReader<R>, attrs: &[OwnedAttribute]) -> Result<UMLModel> {
let mut packages = vec![];
let id = get_attribute(attrs, Some("xmi"), "id")?.into();
let name = get_attribute(attrs, None, "name")?.into();
fn is_package_element(name: &OwnedName, attrs: &[OwnedAttribute]) -> bool {
check_name(name, None, "packagedElement") && check_attribute(&attrs, Some("xmi"), "type", "uml:Package")
}
parse_element(parser, &mut |p, name, attrs| {
if is_package_element(&name, &attrs) {
packages.push(parse_package(p, &attrs)?);
}
Ok(())
})?;
Ok(UMLModel {
id,
name,
packages
})
}
fn find_constraint_by_id<'a>(models: &'a [UMLModel], id: &str) -> Option<&'a UMLConstraint> {
for model in models {
for package in &model.packages {
for class in &package.classess {
for constraint in &class.constraints {
if constraint.id.eq(id) {
return Some(constraint);
}
}
}
}
}
None
}
pub fn parse_uml_model<R: Read + Seek>(project: &mut ZipArchive<R>) -> Result<(Vec<UMLModel>, Vec<UMLModifier>)> {
let mut models = vec![];
let mut modifiers = vec![];
let file = project.by_name("com.nomagic.magicdraw.uml_model.model")?;
let mut parser: MyEventReader<_> = EventReader::new(file).into();
loop {
match parser.next()? {
XmlEvent::StartElement { name, attributes, .. } => {
if check_name(&name, Some("uml"), "Model") {
models.push(parse_model(&mut parser, &attributes)?);
} else if check_name(&name, Some("SQLProfile"), "PrimaryKey") {
let constraint_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Constraint"));
let constraint = unwrap_opt_continue!(find_constraint_by_id(&models, constraint_id));
let property_id = unwrap_opt_continue!(&constraint.property_id).clone();
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
} else if check_name(&name, Some("SQLProfile"), "PKMember") {
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
modifiers.push(UMLModifier::PirmaryKey(UMLPrimaryKeyModifier { property_id }));
} else if check_name(&name, Some("SQLProfile"), "Column") {
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Property")).to_string();
let nullable = unwrap_err_continue!(get_attribute(&attributes, None, "nullable")).eq("true");
modifiers.push(UMLModifier::Nullable(UMLNullableModifier { property_id, nullable }));
} else if check_name(&name, Some("MagicDraw_Profile"), "typeModifier") {
let property_id = unwrap_err_continue!(get_attribute(&attributes, None, "base_Element")).into();
let modifier = unwrap_err_continue!(get_attribute(&attributes, None, "typeModifier")).into();
modifiers.push(UMLModifier::Type(UMLTypeModifier { property_id, modifier }));
} else if check_name(&name, Some("SQLProfile"), "FK") {
let from_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "members")).into();
let to_property_id = unwrap_err_continue!(get_attribute(&attributes, None, "referencedMembers")).into();
modifiers.push(UMLModifier::ForeignKey(UMLForeignKeyModifier { from_property_id, to_property_id }));
}
},
XmlEvent::EndDocument => { break; },
_ => {}
}
}
Ok((models, modifiers))
}

View File

@ -0,0 +1,140 @@
use std::io::Read;
use xml::{EventReader, reader::XmlEvent, name::OwnedName, attribute::OwnedAttribute};
use anyhow::Result;
use thiserror::Error;
pub struct MyEventReader<R: Read> {
depth: u32,
event_reader: EventReader<R>
}
impl<R: Read> MyEventReader<R> {
pub fn next(&mut self) -> Result<XmlEvent> {
let event = self.event_reader.next()?;
if let XmlEvent::StartElement { .. } = event {
self.depth += 1;
} else if let XmlEvent::EndElement { .. } = event {
self.depth -= 1;
}
Ok(event)
}
#[inline(always)]
pub fn depth(&self) -> u32 {
self.depth
}
}
impl<R: Read> From<EventReader<R>> for MyEventReader<R> {
fn from(event_reader: EventReader<R>) -> Self {
MyEventReader {
depth: 0,
event_reader
}
}
}
#[derive(Error, Debug, PartialEq)]
pub enum ParseProjectError {
#[error("XML attribute '{}' not found", format_name_from_parts(.0, .1))]
AttributeNotFound(Option<String>, String),
#[error("Unexpected end of XML document")]
EndOfDocument
}
fn format_name_from_parts(prefix: &Option<String>, local_name: &str) -> String {
if let Some(prefix) = &prefix {
format!("{}:{}", prefix, local_name)
} else {
local_name.into()
}
}
pub fn format_name(name: &OwnedName) -> String {
format_name_from_parts(&name.prefix, &name.local_name)
}
pub fn check_name(name: &OwnedName, prefix: Option<&str>, local_name: &str) -> bool {
name.local_name.eq(local_name) && name.prefix_ref().eq(&prefix)
}
pub fn get_attribute<'a>(attributes: &'a [OwnedAttribute], prefix: Option<&str>, name: &str) -> Result<&'a str, ParseProjectError> {
Ok(attributes.iter()
.find(|attr| check_name(&attr.name, prefix, name))
.map(|attr| &attr.value[..])
.ok_or_else(
|| ParseProjectError::AttributeNotFound(prefix.map(|s| s.to_owned()), name.to_owned()),
)?)
}
#[inline(always)]
pub fn check_attribute(attributes: &[OwnedAttribute], prefix: Option<&str>, name: &str, expected_value: &str) -> bool {
if let Ok(attr) = get_attribute(attributes, prefix, name) {
return attr.eq(expected_value);
}
false
}
pub fn get_element_characters<R: Read>(parser: &mut MyEventReader<R>) -> Result<String> {
let mut parts = vec![];
loop {
match parser.next()? {
XmlEvent::Characters(text) => {
parts.push(text);
},
XmlEvent::EndElement { name } => {
break;
},
_ => {}
}
}
Ok(parts.join(" "))
}
pub fn parse_element<R: Read, F>(parser: &mut MyEventReader<R>, process_element: &mut F) -> Result<()>
where F: FnMut(&mut MyEventReader<R>, OwnedName, Vec<OwnedAttribute>) -> Result<()>
{
let starting_depth = parser.depth();
loop {
match parser.next()? {
XmlEvent::StartElement { name, attributes, .. } => {
process_element(parser, name, attributes)?;
if parser.depth() == starting_depth-1 { break; }
}
XmlEvent::EndElement { name } => {
if parser.depth() == starting_depth-1 { break; }
},
XmlEvent::EndDocument => {
Err(ParseProjectError::EndOfDocument)?
},
_ => {}
}
}
return Ok(());
}
#[macro_export]
macro_rules! unwrap_err_continue {
($res:expr) => {
match $res {
Ok(val) => val,
Err(e) => { continue; }
}
};
}
#[macro_export]
macro_rules! unwrap_opt_continue {
($res:expr) => {
match $res {
Some(val) => val,
None => { continue; }
}
};
}

29
src/main.rs Normal file
View File

@ -0,0 +1,29 @@
use magicdraw_parser::parse_project;
use yew::prelude::*;
use std::fs::File;
use anyhow::Result;
mod magicdraw_parser;
// TODO: Make this work with enumation lookup tables
#[function_component]
fn App() -> Html {
html! {
<main>
<img class="logo" src="https://yew.rs/img/logo.png" alt="Yew logo" />
<h1>{ "Hello World!" }</h1>
<span class="subtitle">{ "from Yew with " }<i class="heart" /></span>
</main>
}
}
fn main() -> Result<()> {
let f = File::open("example.mdzip").unwrap();
let collections = parse_project(f)?;
dbg!(collections);
// yew::Renderer::<App>::new().render();
Ok(())
}