forked from Plugin-JF-Onkostar/osc-variant
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e70b30112b | |||
| 10f737cb41 | |||
| a6657842c4 | |||
| adcce76f5c | |||
| f904f62d45 | |||
| 47be1c7caf | |||
| bf5a3a697f | |||
| 0ec9c85e3a | |||
| 221b2c6a2f | |||
| dc62454a74 | |||
| ee600436f9 | |||
| f858ecb9d4 | |||
| 8c44e623f6 | |||
| 42d1151a6c | |||
| 2d2fb0dc61 | |||
| 036dc80ad0 | |||
| fccfe4a880 | |||
| 9ab00ce192 | |||
| 70562f99ca | |||
| dcfc50878e | |||
| 2358f6410f | |||
| 8f6bbc1692 | |||
| 0a3ca817c6 | |||
| 172a2cc692 | |||
| 7021f94891 | |||
| 6589349b8d | |||
| 2808cc3556 | |||
| f3bbef5d22 | |||
| ef8f6ab1b5 | |||
| a1e5133516 | |||
| 13bcd74f6c | |||
| 6fdbe06106 | |||
| 31c162e977 | |||
| 2413e4b9b8 | |||
| c9ecb8e944 | |||
| 774b57d78e | |||
| a52eb9742e | |||
| 4c76504000 | |||
| 4676a63c69 | |||
| e250e330a5 | |||
| 349b571e09 | |||
| 90423b5b4e | |||
| d45ded939e | |||
| 7ab5523f3c | |||
| f02ea9b065 | |||
| a08abd7688 | |||
| bf7b8cd9ba | |||
| 821b30e452 | |||
| 6a0a356120 | |||
| 352f5e23fc | |||
| 7b13251d34 | |||
| 6da1c48c28 | |||
| e2d5eedd02 | |||
| a55db66e57 | |||
| dce2a5cdda | |||
| 93981f7709 | |||
| b6694b9e53 | |||
| 0e690cbb85 | |||
| 0d6525c398 | |||
| df643b5e60 | |||
| 1746026af8 | |||
| 7cdfe0068f | |||
| 1f5ec80cc6 | |||
| f851e9c424 | |||
| 7ef1638b58 | |||
| 4745a75f2e | |||
| f089ad0b32 | |||
| 3948cdd697 | |||
| 5a04571a16 | |||
| f5dc366488 | |||
| 8c11c6d891 | |||
| 8a23d5b71b | |||
| 5d293555f8 | |||
| f1c8a002fa | |||
| 86894632b6 | |||
| afb5a2e17a | |||
| f456e278cb | |||
| f28ab1afcd | |||
| ae6b8d48e5 | |||
| 1302963b56 | |||
| 6be11a8951 | |||
| 416204b30a | |||
| 86e988c50e | |||
| 76de86b685 | |||
| d0de76770f | |||
| 8cae6ce706 | |||
| 81a0c6204b | |||
| 21e31f35d1 | |||
| 108aae329c | |||
| 1f51d3e994 | |||
| c514005182 | |||
| a127133ba2 | |||
| e2d4d71063 | |||
| 629cc8aba9 | |||
| 298a142586 | |||
| 9b9a0b4622 | |||
| 9ffc0783ff | |||
| 771d99fa27 | |||
| eadf9326d0 | |||
| bf63d93efa | |||
| ce52f92a7f | |||
| b3054f971e | |||
| e27d31a8bf | |||
| d30c2991c0 | |||
| ce8dca1c10 | |||
| bfa7cc3c6b | |||
| 9256e242eb | |||
| e33b1a3a4c | |||
| 42cbb9ce7e | |||
| 8edd50feb4 | |||
| 31eda3efc9 | |||
| 54cea88486 | |||
| 4040c49521 | |||
| e0b16c16d4 | |||
| c07d4c8976 | |||
| 3ec51099c7 | |||
| 1e7a95bb09 | |||
| abdef90e90 | |||
| a2df2650ed | |||
| f947395c54 | |||
| d3d4ec2646 | |||
| f507893b4d | |||
| af4ec8898a | |||
| 7d6a6ee9b3 | |||
| a605018176 | |||
| a8851c5e4f | |||
| 0b0188bd30 | |||
| 1e553aad58 | |||
| f9c66cfdb1 | |||
| 103075ab78 | |||
| 37c8b47d1f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.idea/*
|
.idea/*
|
||||||
/target
|
/target
|
||||||
|
/completion
|
||||||
|
|
||||||
*.iml
|
*.iml
|
||||||
|
|||||||
780
Cargo.lock
generated
780
Cargo.lock
generated
@@ -3,37 +3,183 @@
|
|||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "addr2line"
|
||||||
version = "1.0.1"
|
version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
|
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||||
|
dependencies = [
|
||||||
|
"gimli",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "adler"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "async-trait"
|
||||||
|
version = "0.1.77"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "backtrace"
|
||||||
|
version = "0.3.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||||
|
dependencies = [
|
||||||
|
"addr2line",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"miniz_oxide",
|
||||||
|
"object",
|
||||||
|
"rustc-demangle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8"
|
||||||
|
dependencies = [
|
||||||
|
"bzip2-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bzip2-sys"
|
||||||
|
version = "0.1.11+1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.3.19"
|
version = "4.4.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
|
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
"once_cell",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.3.19"
|
version = "4.4.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
|
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_complete"
|
||||||
version = "4.3.12"
|
version = "4.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
|
checksum = "dfb0d4825b75ff281318c393e8e1b80c4da9fb75a6b1d98547d389d6fe1f48d2"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -43,15 +189,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "console"
|
name = "console"
|
||||||
version = "0.15.7"
|
version = "0.15.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
|
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
@@ -60,6 +206,86 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "constant_time_eq"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deob"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "deranged"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||||
|
dependencies = [
|
||||||
|
"powerfmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dialoguer"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"shell-words",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encode_unicode"
|
name = "encode_unicode"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
@@ -73,10 +299,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "errno"
|
||||||
version = "0.14.0"
|
version = "0.3.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "flate2"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
|
||||||
|
dependencies = [
|
||||||
|
"crc32fast",
|
||||||
|
"miniz_oxide",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gimli"
|
||||||
|
version = "0.28.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@@ -85,20 +353,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "hex"
|
||||||
version = "2.0.0"
|
version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hmac"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "indicatif"
|
||||||
version = "1.0.9"
|
version = "0.17.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"instant",
|
||||||
|
"number_prefix",
|
||||||
|
"portable-atomic",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
@@ -108,48 +431,125 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.147"
|
version = "0.2.152"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "miniz_oxide"
|
||||||
version = "1.18.0"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "osc-variant"
|
|
||||||
version = "0.2.0"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"adler",
|
||||||
"console",
|
|
||||||
"quick-xml",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
"xml-rs",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "number_prefix"
|
||||||
version = "1.0.66"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
|
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "object"
|
||||||
|
version = "0.32.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "osc-variant"
|
||||||
|
version = "0.7.0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"clap",
|
||||||
|
"clap_complete",
|
||||||
|
"console",
|
||||||
|
"deob",
|
||||||
|
"dialoguer",
|
||||||
|
"indicatif",
|
||||||
|
"quick-xml",
|
||||||
|
"serde",
|
||||||
|
"serde_yaml",
|
||||||
|
"sha256",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "password-hash"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pbkdf2"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"hmac",
|
||||||
|
"password-hash",
|
||||||
|
"sha2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "powerfmt"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.76"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.30.0"
|
version = "0.31.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
|
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -157,33 +557,67 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.32"
|
version = "1.0.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
|
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "rand_core"
|
||||||
version = "1.0.15"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-demangle"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.182"
|
version = "1.0.195"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bdb30a74471f5b7a1fa299f40b4bf1be93af61116df95465b2b5fc419331e430"
|
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.182"
|
version = "1.0.195"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f4c2c6ea4bc09b5c419012eafcdb0fcef1d9119d626c8f3a0708a5b92d38a70"
|
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -192,9 +626,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_yaml"
|
name = "serde_yaml"
|
||||||
version = "0.9.25"
|
version = "0.9.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
|
checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"itoa",
|
"itoa",
|
||||||
@@ -204,10 +638,57 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "sha1"
|
||||||
version = "2.0.28"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
|
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha256"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"hex",
|
||||||
|
"sha2",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-words"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -215,37 +696,111 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "tempfile"
|
||||||
version = "1.0.11"
|
version = "3.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
|
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"redox_syscall",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
|
||||||
|
dependencies = [
|
||||||
|
"deranged",
|
||||||
|
"powerfmt",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "1.35.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||||
|
dependencies = [
|
||||||
|
"backtrace",
|
||||||
|
"bytes",
|
||||||
|
"pin-project-lite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.10"
|
version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unsafe-libyaml"
|
name = "unsafe-libyaml"
|
||||||
version = "0.2.9"
|
version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
@@ -258,48 +813,97 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.42.2"
|
version = "0.52.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xml-rs"
|
name = "zeroize"
|
||||||
version = "0.8.16"
|
version = "1.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"
|
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "0.6.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"byteorder",
|
||||||
|
"bzip2",
|
||||||
|
"constant_time_eq",
|
||||||
|
"crc32fast",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"flate2",
|
||||||
|
"hmac",
|
||||||
|
"pbkdf2",
|
||||||
|
"sha1",
|
||||||
|
"time",
|
||||||
|
"zstd",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd"
|
||||||
|
version = "0.11.2+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4"
|
||||||
|
dependencies = [
|
||||||
|
"zstd-safe",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-safe"
|
||||||
|
version = "5.0.2+zstd.1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"zstd-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zstd-sys"
|
||||||
|
version = "2.0.9+zstd.1.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|||||||
38
Cargo.toml
38
Cargo.toml
@@ -1,19 +1,38 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "osc-variant"
|
name = "osc-variant"
|
||||||
version = "0.2.0"
|
version = "0.7.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"]
|
authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"]
|
||||||
description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort"
|
description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["libs/deob"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.3", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
|
clap = { version = "4.4", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
quick-xml = { version = "0.30", features = ["escape-html", "serialize"], default-features = false }
|
quick-xml = { version = "0.31", features = ["escape-html", "serialize"], default-features = false }
|
||||||
xml-rs = "0.8"
|
|
||||||
console = "0.15"
|
console = "0.15"
|
||||||
|
sha256 = "1.4"
|
||||||
|
dialoguer = "0.11"
|
||||||
|
indicatif = "0.17"
|
||||||
|
|
||||||
|
deob = { path = "./libs/deob", optional = true }
|
||||||
|
zip = { version = "0.6", optional = true }
|
||||||
|
bytes = "1.5.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# Requires env var OSB_KEY to be present at build time
|
||||||
|
unzip-osb = ["dep:deob", "dep:zip"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
clap = { version = "4.4", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
|
||||||
|
clap_complete = "4.4"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
@@ -24,3 +43,14 @@ panic = "abort"
|
|||||||
|
|
||||||
[package.metadata.deb]
|
[package.metadata.deb]
|
||||||
copyright = "Copyright (c) 2023 Comprehensive Cancer Center Mainfranken"
|
copyright = "Copyright (c) 2023 Comprehensive Cancer Center Mainfranken"
|
||||||
|
extended-description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort."
|
||||||
|
assets = [
|
||||||
|
["target/release/osc-variant", "usr/bin/", "755"],
|
||||||
|
["completion/osc-variant.bash", "etc/bash_completion.d/", "644"]
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata.generate-rpm]
|
||||||
|
assets = [
|
||||||
|
{ source = "target/release/osc-variant", dest = "/usr/bin/", mode = "755" },
|
||||||
|
{ source = "completion/osc-variant.bash", dest = "/etc/bash_completion.d/", mode = "644" }
|
||||||
|
]
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -32,11 +32,11 @@ binary-all: win-binary-x86_64 linux-binary-x86_64
|
|||||||
|
|
||||||
.PHONY: win-binary-x86_64
|
.PHONY: win-binary-x86_64
|
||||||
win-binary-x86_64:
|
win-binary-x86_64:
|
||||||
cargo build --release --target=x86_64-pc-windows-gnu
|
cargo build --release --target=x86_64-pc-windows-gnu --features unzip-osb
|
||||||
|
|
||||||
.PHONY: linux-binary-x86_64
|
.PHONY: linux-binary-x86_64
|
||||||
linux-binary-x86_64:
|
linux-binary-x86_64:
|
||||||
cargo build --release --target=x86_64-unknown-linux-gnu
|
cargo build --release --target=x86_64-unknown-linux-gnu --features unzip-osb
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
|
|||||||
130
README.md
130
README.md
@@ -16,12 +16,72 @@ unvollständigen Ausgabedateien zu erzeugen.
|
|||||||
|
|
||||||
### Beispiele
|
### Beispiele
|
||||||
|
|
||||||
|
Die folgenden Unterbefehle sind verfügbar
|
||||||
|
|
||||||
|
```
|
||||||
|
Anwendung zum Anpassen einer OSC-Datei an einen Standort
|
||||||
|
|
||||||
|
Usage: osc-variant <COMMAND>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
sha256sum Berechne SHA256 Prüfsumme für die angegebene Datei
|
||||||
|
list Zeigt alle enthaltenen Kataloge und Formulare mit Revision an.
|
||||||
|
tree Zeigt Kataloge und Formulare mit Revision und Abhängigkeiten an.
|
||||||
|
modify Modifiziert die angegebene Datei anhand der Profildatei
|
||||||
|
diff Vergleiche zwei Dateien anhand der Revision der enthaltenen Inhalte
|
||||||
|
check Prüfe eine OSC-Datei auf bekannte Problemen
|
||||||
|
help Print this message or the help of the given subcommand(s)
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help Print help
|
||||||
|
-V, --version Print version
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Unterbefehl `sha256sum`
|
||||||
|
|
||||||
|
Das Berechnen der SHA256 Prüfsumme ist mit dem Unterbefehl `sha256sum` auch unter Windows einfach möglich
|
||||||
|
und erzeugt eine Ausgabe analog dem Befehl auf Linux-Systemen:
|
||||||
|
|
||||||
|
```
|
||||||
|
osc-variant sha256sum meine-beispieldatei.osc
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Unterbefehl `list`
|
||||||
|
|
||||||
Zum Auflisten der Inhalte einer Datei wird folgender Befehl verwendet:
|
Zum Auflisten der Inhalte einer Datei wird folgender Befehl verwendet:
|
||||||
|
|
||||||
```
|
```
|
||||||
osc-variant list meine-beispieldatei.osc
|
osc-variant list meine-beispieldatei.osc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Mit der Option `--filter` kann die Ausgabe eingeschränkt werden.
|
||||||
|
|
||||||
|
*Bei Verwendung der OSB-Funktionalität kann die Eingabe eines Passworts erforderlich sein.*
|
||||||
|
|
||||||
|
#### Unterbefehl `tree`
|
||||||
|
|
||||||
|
Zum Auflisten der Inhalte mit allen Abhängigkeiten, z.B. Daten- und Merkmalskataloge und bei Formularen wird der Befehl
|
||||||
|
`tree` verwendet:
|
||||||
|
|
||||||
|
```
|
||||||
|
osc-variant tree meine-beispieldatei.osc
|
||||||
|
```
|
||||||
|
|
||||||
|
Abhängigkeiten werden je nach Art gekennzeichnet:
|
||||||
|
|
||||||
|
* `+`: Datenkatalog
|
||||||
|
* `-`: Merkmalskatalog
|
||||||
|
* `>`: Formularverweis
|
||||||
|
* `*`: Unterformular
|
||||||
|
|
||||||
|
Für Formularverweise und Unterformulare werden dabei die verwendeten Datenkataloge nicht erneut ausgegeben.
|
||||||
|
|
||||||
|
Achtung! Dies erzeugt eine sehr umfangreiche Ausgabe.
|
||||||
|
|
||||||
|
Mit der Option `--filter` kann auch hier die Ausgabe eingeschränkt werden.
|
||||||
|
|
||||||
|
#### Unterbefehl `diff`
|
||||||
|
|
||||||
Zum Vergleich zweier OSC-Dateien wird der Unterbefehl `diff` verwendet.
|
Zum Vergleich zweier OSC-Dateien wird der Unterbefehl `diff` verwendet.
|
||||||
Der optionale Parameter `--strict` vergleicht auch den Inhalt der OSC-Datei.
|
Der optionale Parameter `--strict` vergleicht auch den Inhalt der OSC-Datei.
|
||||||
Ohne diesen wird nur das Vorhandensein von Inhalten und die Revision verglichen.
|
Ohne diesen wird nur das Vorhandensein von Inhalten und die Revision verglichen.
|
||||||
@@ -36,6 +96,8 @@ bzw.
|
|||||||
osc-variant diff meine-beispieldatei.osc andere-beispieldatei.osc --strict
|
osc-variant diff meine-beispieldatei.osc andere-beispieldatei.osc --strict
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Unterbefehl `modify`
|
||||||
|
|
||||||
Zum Anpassen des Inhalts einer Datei:
|
Zum Anpassen des Inhalts einer Datei:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -43,27 +105,74 @@ osc-variant modify meine-beispieldatei.osc --profile ukw-profil.yml --output ukw
|
|||||||
```
|
```
|
||||||
|
|
||||||
Die Parameter `--profile` und `--output` sind optional.
|
Die Parameter `--profile` und `--output` sind optional.
|
||||||
|
Mit dem ebenfalls optionalen Parameter `--interactve` oder `-i` können die Parameter zur kompakten Ausgabe, zum Sortieren
|
||||||
|
und dem Entfernen von Inhalten der Systembibliothek interaktiv gesetzt werden.
|
||||||
|
|
||||||
Ohne Profildatei wird die Datei lediglich eingelesen, Leerzeichen am Ende eines XML-Tags entfernt und wieder ausgegeben.
|
Ohne Profildatei wird die Datei lediglich eingelesen, Leerzeichen am Ende eines XML-Tags entfernt und wieder ausgegeben.
|
||||||
|
|
||||||
Ohne eine Angabe der Ausgabedatei wird auf die Standardausgabe ausgegeben.
|
Ohne eine Angabe der Ausgabedatei wird auf die Standardausgabe ausgegeben.
|
||||||
|
|
||||||
|
##### Enthaltene Profile
|
||||||
|
|
||||||
|
Die im Ordner [`examples/`](/examples) enthaltenen Profile für Standorte sind in der ausführbaren Anwendung enthalten
|
||||||
|
und die Dateien müssen nicht explizit als Datei vorliegen:
|
||||||
|
|
||||||
|
* `--profile examples/dnpm-ukm.yml` => `--profile UKM`
|
||||||
|
* `--profile examples/dnpm-ukw.yml` => `--profile UKW`
|
||||||
|
* `--profile examples/dnpm-umg.yml` => `--profile UMG`
|
||||||
|
|
||||||
|
#### Unterbefehl `unzip-osb`
|
||||||
|
|
||||||
|
Ab Version 0.6.0 ist die Anwendung zudem in der Lage, die für eine Aktualisierung der OS-Bibliothek genutzten OSB-Dateien zu entpacken:
|
||||||
|
|
||||||
|
```
|
||||||
|
osc-variant unzip-osb OSBIB-6.10.osb
|
||||||
|
```
|
||||||
|
|
||||||
|
Dieser Befehl kennt die beiden optionalen Parameter
|
||||||
|
|
||||||
|
* `-d`: Optionale Angabe des Zielverzeichnisses. Wenn keine Angabe vorhanden ist, wird das aktuelle Verzeichnis verwendet.
|
||||||
|
* `-p`/`--password`: Optionale Angabe des Passworts zum Entpacken der OSB-Datei.
|
||||||
|
|
||||||
|
#### Unterbefehl `check`
|
||||||
|
|
||||||
|
Der Unterbefehl `check` prüft eine OSC-Datei auf bekannte Probleme und gibt eine Liste mit erkannten Problemen aus.
|
||||||
|
|
||||||
|
Eine Liste mit bekannten Problemen wird mit `check --list` ausgegeben.
|
||||||
|
|
||||||
|
*Bei Verwendung der OSB-Funktionalität kann die Eingabe eines Passworts erforderlich sein.*
|
||||||
|
|
||||||
#### Kompakte Ausgabe
|
#### Kompakte Ausgabe
|
||||||
|
|
||||||
OSC-Dateien sind XML-Dateien. Diese Anwendung ermöglicht optional die Ausgabe als kompaktere XML-Datei ohne Zeilenumbrüche.
|
OSC-Dateien sind XML-Dateien. Diese Anwendung ermöglicht optional die Ausgabe als kompaktere XML-Datei ohne Zeilenumbrüche.
|
||||||
Hierzu ist die Option `--compact` vorgesehen. Es können, je nach Datei, bis zu 30% eingespart werden.
|
Hierzu ist die Option `--compact` vorgesehen. Es können, je nach Datei, bis zu 30 % eingespart werden.
|
||||||
|
|
||||||
|
#### Filter
|
||||||
|
|
||||||
|
Bei der Auflistung von Inhalten ist es möglich, die Anzeige für die Unterbefehle `list` und `tree` anhand des Namens zu filtern.
|
||||||
|
Hierzu ist die Option `--filter=` vorgesehen.
|
||||||
|
Wird diese angewendet, werden nur Inhalte angezeigt, deren Name die angegebene Zeichenkette beinhalten.
|
||||||
|
|
||||||
#### Sortierung
|
#### Sortierung
|
||||||
|
|
||||||
Bei der Auflistung der Inhalte, kann die Option `--sorted` dazu verwendet werden, die angezeigten Einträge alphabetisch zu sortieren.
|
Bei der Auflistung der Inhalte, kann die Option `--sorted` dazu verwendet werden, die angezeigten Einträge alphabetisch zu sortieren.
|
||||||
Die Sortierung erfolgt dabei nach Namen des Katalogs oder des Formulars.
|
Die Sortierung erfolgt dabei nach Namen des Katalogs oder des Formulars.
|
||||||
|
|
||||||
##### Experimentell: Sortierung nach Modifikation
|
Beim Modifizieren der Inhalte kann ebenfalls die Option `--sorted` dazu verwendet werden, die Einträge im Anschluss an die Modifikation
|
||||||
|
nach Namen und für Formulare der Abhängigkeit von Formularverweisen und Unterformularen zu sortieren.
|
||||||
|
|
||||||
|
Formulare, die von anderen Formularen in einem Formularverweis oder als Unterformular verwendet werden, werden dabei weiter oben angeordnet,
|
||||||
|
da Onkostar einen Formularimport sequenziell, ohne Berücksichtigung von Abhängigkeiten, durchführt.
|
||||||
|
|
||||||
Beim Modifizieren der Inhalte kann die experimentelle Option `--x-sorted` dazu verwendet werden, die Einträge im Anschluss an die Modifikation
|
|
||||||
nach Namen zu sortieren.
|
|
||||||
Dies erlaubt eine konsistente Reihenfolge der Einträge, wodurch ein direkter Vergleich mit Vorversionen ermöglicht wird.
|
Dies erlaubt eine konsistente Reihenfolge der Einträge, wodurch ein direkter Vergleich mit Vorversionen ermöglicht wird.
|
||||||
ACHTUNG: Es kann sein, dass dadurch ein Import der resultierenden OSC-Datei nicht mehr möglich ist, da das genaue Verhalten des Imports aktuell noch nicht bekannt ist.
|
|
||||||
|
*Die Einteilung in Formualre und Unterformualare wird hierdurch nicht angepasst.*
|
||||||
|
|
||||||
|
##### Entfernen von Inhalten der Systembibliothek bei Modifikation
|
||||||
|
|
||||||
|
Mit der die experimentelle Option `--strip` ist es möglich, die in der OSC-Datei enthaltenen und beim Import nicht genutzten Inhalte aus der Systembibliothek zu entfernen.
|
||||||
|
|
||||||
|
Hierbei werden alle Inhalte entfernt, die im Ordner "ONKOSTAR Bibliothek" enthalten sind, beim Import jedoch ignoriert werden.
|
||||||
|
|
||||||
## Profile
|
## Profile
|
||||||
|
|
||||||
@@ -74,6 +183,9 @@ In ihr sind die durchzuführenden Änderungen definiert. Eine Profildatei hat di
|
|||||||
```
|
```
|
||||||
forms:
|
forms:
|
||||||
- name: "ExampleForm"
|
- name: "ExampleForm"
|
||||||
|
form_field:
|
||||||
|
- name: "formularfeld"
|
||||||
|
hide: true
|
||||||
form_references:
|
form_references:
|
||||||
- name: "ref_first_mtb"
|
- name: "ref_first_mtb"
|
||||||
referenced_data_form: "Formularverweis.Variante"
|
referenced_data_form: "Formularverweis.Variante"
|
||||||
@@ -100,6 +212,12 @@ und dabei die vorhandenen Angaben für den Formularverweis zu ersetzen.
|
|||||||
Die Angaben für `referenced_data_form`, `anzeige_auswahl`, `anzeige` und `scripts_code` sind optional.
|
Die Angaben für `referenced_data_form`, `anzeige_auswahl`, `anzeige` und `scripts_code` sind optional.
|
||||||
Wird keine Angabe gemacht, wird der bestehende Wert beibehalten.
|
Wird keine Angabe gemacht, wird der bestehende Wert beibehalten.
|
||||||
|
|
||||||
|
Zudem wird im Formular "ExampleForm" das Formularfeld "formularfeld" ausgeblendet, indem der Filter auf "false" gesetzt wird.
|
||||||
|
Dadurch wird das Formularfeld nie angezeigt.
|
||||||
|
Ein zuvor bestehender Filter wird ersetzt.
|
||||||
|
Weiterhin wird die Eigenschaft "Speichern" des Formularfelds auf "Immer speichern" gesetzt um sicherzustellen, dass zuvor
|
||||||
|
enthaltene Daten weiterhin gespeichert bleiben und werden, auch wenn das Formularfeld nicht sichtbar ist.
|
||||||
|
|
||||||
**Achtung!** Diese Anwendung überprüft keine Scripts und verwendet angegebene Scripts als "valid" im resultierenden OSC-File.
|
**Achtung!** Diese Anwendung überprüft keine Scripts und verwendet angegebene Scripts als "valid" im resultierenden OSC-File.
|
||||||
|
|
||||||
Zudem kann die Menükategorie angepasst werden.
|
Zudem kann die Menükategorie angepasst werden.
|
||||||
@@ -109,4 +227,4 @@ Wird sie angeben, sind die Felder `name`, `position` und `column` verpflichtend.
|
|||||||
Es können beliebig viele Formulare mit beliebig vielen Änderungen zu Formularverweisen in einer Profildatei
|
Es können beliebig viele Formulare mit beliebig vielen Änderungen zu Formularverweisen in einer Profildatei
|
||||||
hinterlegt werden, jedoch ist mindestens eine Angabe zu einem Formularfeld erforderlich.
|
hinterlegt werden, jedoch ist mindestens eine Angabe zu einem Formularfeld erforderlich.
|
||||||
|
|
||||||
Beispiele für eine Profildatei sind unter [`examples/`](examples/) zu finden.
|
Beispiele für eine Profildatei sind unter [`examples/`](/examples) zu finden.
|
||||||
|
|||||||
44
build.rs
Normal file
44
build.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
|
use clap_complete::generate_to;
|
||||||
|
use clap_complete::Shell::Bash;
|
||||||
|
|
||||||
|
include!("src/cli.rs");
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
let mut cmd = build_cli();
|
||||||
|
|
||||||
|
let package_name = std::env::var("CARGO_CRATE_NAME").unwrap_or("osc-variant".to_string());
|
||||||
|
|
||||||
|
fs::remove_dir_all("completion").unwrap_or_default();
|
||||||
|
fs::create_dir("completion")?;
|
||||||
|
|
||||||
|
generate_to(Bash, &mut cmd, package_name.as_str(), "completion")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
27
examples/dnpm-master.yml
Normal file
27
examples/dnpm-master.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
forms:
|
||||||
|
- name: 'DNPM Klinik/Anamnese'
|
||||||
|
form_references:
|
||||||
|
- name: MTB
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
- name: 'DNPM Therapieplan'
|
||||||
|
form_references:
|
||||||
|
- name: referstemtb
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
- name: reftkhumangenber
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
- name: reftkreevaluation
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
- name: 'DNPM UF Einzelempfehlung'
|
||||||
|
form_references:
|
||||||
|
- name: mtb
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
- name: 'DNPM UF Rebiopsie'
|
||||||
|
form_references:
|
||||||
|
- name: reftumorkonferenz
|
||||||
|
referenced_data_form: 'OS.Tumorkonferenz'
|
||||||
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
@@ -4,6 +4,10 @@ forms:
|
|||||||
- name: MTB
|
- name: MTB
|
||||||
referenced_data_form: 'MR.MTB_Anmeldung'
|
referenced_data_form: 'MR.MTB_Anmeldung'
|
||||||
anzeige_auswahl: 'MTB Anmeldung vom {Anmeldedatum}'
|
anzeige_auswahl: 'MTB Anmeldung vom {Anmeldedatum}'
|
||||||
|
scripts_code: |
|
||||||
|
setFieldValue('AnmeldedatumMTB', getFieldValue('MTB').MTBTermin);
|
||||||
|
setFieldValue('WHOGrad', getFieldValue('MTB').WHOGrad);
|
||||||
|
setFieldValue('Leitlinienstatus', getFieldValue('MTB').Leitlinienstatus);
|
||||||
- name: 'DNPM Therapieplan'
|
- name: 'DNPM Therapieplan'
|
||||||
form_references:
|
form_references:
|
||||||
- name: referstemtb
|
- name: referstemtb
|
||||||
|
|||||||
@@ -4,6 +4,13 @@ forms:
|
|||||||
- name: MTB
|
- name: MTB
|
||||||
referenced_data_form: 'OS.Tumorkonferenz.VarianteUKW'
|
referenced_data_form: 'OS.Tumorkonferenz.VarianteUKW'
|
||||||
anzeige_auswahl: 'MTB vom {Datum}'
|
anzeige_auswahl: 'MTB vom {Datum}'
|
||||||
|
script_code: |
|
||||||
|
// Keine Übernahme des Datums des MTBs bei Verwendung
|
||||||
|
// des Formulars 'OS.Tumorkonferenz'.
|
||||||
|
// Gewollt ist das Datum, an der die Anmeldung stattgefunden hat.
|
||||||
|
//setFieldValue('AnmeldedatumMTB', getFieldValue('MTB').Datum);
|
||||||
|
|
||||||
|
setFieldValue('WHOGrad', getFieldValue('MTB').WHOGrad);
|
||||||
- name: 'DNPM Therapieplan'
|
- name: 'DNPM Therapieplan'
|
||||||
form_references:
|
form_references:
|
||||||
- name: referstemtb
|
- name: referstemtb
|
||||||
|
|||||||
7
libs/deob/Cargo.toml
Normal file
7
libs/deob/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "deob"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0"
|
||||||
32
libs/deob/build.rs
Normal file
32
libs/deob/build.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Error> {
|
||||||
|
cc::Build::new().file("src/deob.c").compile("deob");
|
||||||
|
println!("cargo:rerun-if-changed=src/deob.c");
|
||||||
|
println!("cargo:rerun-if-changed=src/deob.h");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
37
libs/deob/src/deob.c
Normal file
37
libs/deob/src/deob.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "deob.h"
|
||||||
|
|
||||||
|
void deob(char * in) {
|
||||||
|
const long long s[2] = { S0, S1 };
|
||||||
|
char d[] = "OSTAR.password$OSB";
|
||||||
|
for (size_t i = 0; i < DL; i++) d[i] = (CS)[i];
|
||||||
|
size_t l = strlen(in) / 2;
|
||||||
|
for (size_t i = 0; i < l; i++) {
|
||||||
|
for (size_t j = 0; j < DL; j++) { DLT(0); DLT(1); }
|
||||||
|
DLS(i);
|
||||||
|
}
|
||||||
|
INZ(l);
|
||||||
|
}
|
||||||
43
libs/deob/src/deob.h
Normal file
43
libs/deob/src/deob.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OSC_VARIANT_DEOB_H
|
||||||
|
#define OSC_VARIANT_DEOB_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define S0 8373972096940928081
|
||||||
|
#define S1 7378413942531504450
|
||||||
|
#define CS (char*)&s
|
||||||
|
#define DL sizeof(d) / sizeof(d[0]) - 2
|
||||||
|
#define I2 i*2
|
||||||
|
#define INZ(var) in[var] = 0
|
||||||
|
#define DLS(idx) in[idx] = (d[DL+1]<<4)|d[DL]
|
||||||
|
#define DLT(idx) d[DL+idx] = (in[I2+idx] == d[j]) ? (char)j : d[DL+idx]
|
||||||
|
|
||||||
|
void deob(char * in);
|
||||||
|
|
||||||
|
#endif //OSC_VARIANT_DEOB_H
|
||||||
38
libs/deob/src/lib.rs
Normal file
38
libs/deob/src/lib.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::ffi::{c_char, CStr, CString};
|
||||||
|
|
||||||
|
#[link(name = "deob")]
|
||||||
|
extern "C" {
|
||||||
|
fn deob(key: *const c_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deobfuscate(s: &str) -> String {
|
||||||
|
let key = CString::new(s).unwrap_or_default().into_raw();
|
||||||
|
unsafe {
|
||||||
|
deob(key);
|
||||||
|
String::from_utf8_lossy(CStr::from_ptr(key).to_bytes()).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
30
osc-variant.wxs
Normal file
30
osc-variant.wxs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||||
|
<Product Id="*"
|
||||||
|
Language="1033"
|
||||||
|
Manufacturer="CCC Mainfranken"
|
||||||
|
Name="OSC-Variant"
|
||||||
|
UpgradeCode="{83088581-5db3-49a2-8932-27da356818c7}"
|
||||||
|
Version="0.6.0">
|
||||||
|
|
||||||
|
<Package InstallScope="perMachine" Compressed="yes" />
|
||||||
|
|
||||||
|
<MediaTemplate EmbedCab="yes" />
|
||||||
|
|
||||||
|
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||||
|
<Directory Id="ProgramFiles64Folder">
|
||||||
|
<Directory Id="INSTALLFOLDER" Name="osc-variant">
|
||||||
|
<Component Id="MainExecutable" Guid="*">
|
||||||
|
<File Id="OscVariantExe" Name="osc-variant.exe" KeyPath="yes" Source="target/x86_64-pc-windows-gnu/release/osc-variant.exe" />
|
||||||
|
<File Id="LicenseTxt" Name="LICENSE.txt" Source="LICENSE.txt" />
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Feature Id="Complete">
|
||||||
|
<ComponentRef Id="MainExecutable" />
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
</Product>
|
||||||
|
</Wix>
|
||||||
245
src/checks/mod.rs
Normal file
245
src/checks/mod.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
pub mod osb;
|
||||||
|
pub mod osc;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum CheckNotice {
|
||||||
|
/// This will result in Error if importing file and has a support code
|
||||||
|
ErrorWithCode {
|
||||||
|
code: String,
|
||||||
|
description: String,
|
||||||
|
line: Option<usize>,
|
||||||
|
example: Option<String>,
|
||||||
|
},
|
||||||
|
/// This will result in Error if importing file
|
||||||
|
Error {
|
||||||
|
description: String,
|
||||||
|
line: Option<usize>,
|
||||||
|
},
|
||||||
|
/// Other known issues
|
||||||
|
Warning {
|
||||||
|
description: String,
|
||||||
|
line: Option<usize>,
|
||||||
|
},
|
||||||
|
/// Other known issues
|
||||||
|
Info {
|
||||||
|
description: String,
|
||||||
|
line: Option<usize>,
|
||||||
|
},
|
||||||
|
/// Ok
|
||||||
|
Ok(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for CheckNotice {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CheckNotice::ErrorWithCode {
|
||||||
|
code,
|
||||||
|
description,
|
||||||
|
line,
|
||||||
|
example,
|
||||||
|
} => match line {
|
||||||
|
Some(line) => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} ({}) at Line {}: {}{}",
|
||||||
|
style("ERROR").red().bold(),
|
||||||
|
code,
|
||||||
|
line,
|
||||||
|
description,
|
||||||
|
match example {
|
||||||
|
Some(example) => format!("\n 🔥 '{}'", style(example).dim()),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
None => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} ({}): {}{}",
|
||||||
|
style("ERROR").red().bold(),
|
||||||
|
code,
|
||||||
|
description,
|
||||||
|
match example {
|
||||||
|
Some(example) => format!("\n 🔥 '{}'", style(example).dim()),
|
||||||
|
_ => String::new(),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
},
|
||||||
|
CheckNotice::Error { description, line } => match line {
|
||||||
|
Some(line) => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} at Line {}: {}",
|
||||||
|
style("ERROR").red().bold(),
|
||||||
|
line,
|
||||||
|
description
|
||||||
|
),
|
||||||
|
None => write!(f, "{: <7} {}", style("ERROR").red().bold(), description),
|
||||||
|
},
|
||||||
|
CheckNotice::Warning { description, line } => match line {
|
||||||
|
Some(line) => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} at Line {}: {}",
|
||||||
|
style("WARNING").yellow().bold(),
|
||||||
|
line,
|
||||||
|
description
|
||||||
|
),
|
||||||
|
None => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} {}",
|
||||||
|
style("WARNING").yellow().bold(),
|
||||||
|
description
|
||||||
|
),
|
||||||
|
},
|
||||||
|
CheckNotice::Info { description, line } => match line {
|
||||||
|
Some(line) => write!(
|
||||||
|
f,
|
||||||
|
"{: <7} at Line {}: {}",
|
||||||
|
style("INFO").blue().bold(),
|
||||||
|
line,
|
||||||
|
description
|
||||||
|
),
|
||||||
|
None => write!(f, "{: <7} {}", style("INFO").blue().bold(), description),
|
||||||
|
},
|
||||||
|
CheckNotice::Ok(msg) => write!(f, "{: <7} {}", style("OK").green(), msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Checkable {
|
||||||
|
fn check(&self) -> Vec<CheckNotice>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Fixable {
|
||||||
|
fn fix(&mut self) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn check_file(file: &Path, password: Option<String>) -> Result<Vec<CheckNotice>, CheckNotice> {
|
||||||
|
match file.extension() {
|
||||||
|
Some(ex) => match ex.to_str() {
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
Some("osb") => match password {
|
||||||
|
Some(password) => osb::check_file(file, password.as_str()),
|
||||||
|
None => {
|
||||||
|
use deob::deobfuscate;
|
||||||
|
osb::check_file(file, deobfuscate(env!("OSB_KEY").trim()).as_str())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some("osc") => osc::check_file(file),
|
||||||
|
_ => Err(CheckNotice::Error {
|
||||||
|
description: "Keine prüfbare Datei".to_string(),
|
||||||
|
line: None,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
_ => Err(CheckNotice::Error {
|
||||||
|
description: "Keine prüfbare Datei".to_string(),
|
||||||
|
line: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_checks() {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
style("Die folgenden Probleme sind bekannt\n")
|
||||||
|
.yellow()
|
||||||
|
.bold()
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Problem<'a> {
|
||||||
|
code: &'a str,
|
||||||
|
name: &'a str,
|
||||||
|
description: &'a str,
|
||||||
|
fixable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Display for Problem<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{} {} {}\n\n{}",
|
||||||
|
style(self.code).bold(),
|
||||||
|
style(self.name).underlined(),
|
||||||
|
match self.fixable {
|
||||||
|
true => style("(Behebbar)").green(),
|
||||||
|
false => style("(Nicht behebbar)").red(),
|
||||||
|
},
|
||||||
|
self.description
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Problem {
|
||||||
|
code: "2023-0001",
|
||||||
|
name: "Unterformular mit Markierung 'hat Unterformulare'",
|
||||||
|
description: " Aktuell gibt es keine Unterformulare in Unterformularen, daher\n \
|
||||||
|
sollte dies nicht vorkommen.\n\n \
|
||||||
|
Eine mögliche Ursache ist die Speicherung eines Unterformulars als Formular.",
|
||||||
|
fixable: false,
|
||||||
|
},
|
||||||
|
Problem {
|
||||||
|
code: "2023-0002",
|
||||||
|
name: "Formular hat keine Angabe zum Prozedurdatum",
|
||||||
|
description: " Formulare benötigen die Angabe des Prozedurdatums, anderenfalls\n \
|
||||||
|
führt dies zu Problemen in Onkostar.\n\n \
|
||||||
|
Unterformulare können ein Prozedurdatum haben, müssen es aber nicht.\n\n \
|
||||||
|
Eine mögliche Ursache ist die Speicherung eines Formulars als Unterformular.",
|
||||||
|
fixable: false,
|
||||||
|
},
|
||||||
|
Problem {
|
||||||
|
code: "2023-0003",
|
||||||
|
name: "Leerzeichen am Ende der Plausibilitätsregel-Bezeichnung (OSTARSUPP-13334)",
|
||||||
|
description:
|
||||||
|
" Treten Leerzeichen am Ende der Plausibilitätsregel-Bezeichnung auf,\n \
|
||||||
|
führt dies zu Fehlern beim Import der OSC-Datei.\n\n \
|
||||||
|
Das Problem wird beim Verwenden des Unterbefehls 'modify' automatisch\n \
|
||||||
|
behoben und Leerzeichen entfernt.
|
||||||
|
",
|
||||||
|
fixable: true,
|
||||||
|
},
|
||||||
|
Problem {
|
||||||
|
code: "2023-0004",
|
||||||
|
name: "Verweis auf noch nicht definiertes Formular (OSTARSUPP-13212)",
|
||||||
|
description: " Wenn ein Formular einen Verweis auf ein anderes Formular enthält,\n \
|
||||||
|
das nicht vor diesem Formular in der OSC-Datei definiert ist, wird der\n \
|
||||||
|
Formularverweis beim Import der OSC-Datei nicht übernommen.\n\n \
|
||||||
|
Dies kann bei wechselseitiger Abhängigkeit zwischen zwei (Unter-)Formularen\n \
|
||||||
|
auftreten.\n\n \
|
||||||
|
In diesem Fall kann ein erneuter/zweiter Import helfen, da das Onkostar in\n \
|
||||||
|
diesem Fall alle Formulare importiert hat und der Formularverweis dann \n \
|
||||||
|
gespeichert werden kann.
|
||||||
|
",
|
||||||
|
fixable: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.for_each(|problem| println!("{}\n", problem))
|
||||||
|
}
|
||||||
113
src/checks/osb.rs
Normal file
113
src/checks/osb.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
||||||
|
use crate::checks::{osc, CheckNotice};
|
||||||
|
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
pub fn check_file(file: &Path, password: &str) -> Result<Vec<CheckNotice>, CheckNotice> {
|
||||||
|
let file = match fs::File::open(file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(CheckNotice::Error {
|
||||||
|
description: format!("Kann Datei nicht lesen: {}", err),
|
||||||
|
line: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut archive = match zip::ZipArchive::new(file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
return Err(CheckNotice::Error {
|
||||||
|
description: format!("Kann Datei nicht lesen: {}", err),
|
||||||
|
line: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
let progress_bar = ProgressBar::new(archive.len() as u64).with_style(
|
||||||
|
ProgressStyle::default_bar()
|
||||||
|
.template("{wide_bar} {msg:32} {pos}/{len}")
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
progress_bar.inc(1);
|
||||||
|
if let Ok(Ok(mut zip_file)) = archive.by_index_decrypt(i, password.as_bytes()) {
|
||||||
|
progress_bar.set_message(zip_file.name().to_string());
|
||||||
|
if zip_file.is_file() && zip_file.name().ends_with(".osc") {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let _ = zip_file.read_to_string(&mut buf);
|
||||||
|
match osc::check(buf) {
|
||||||
|
Ok(ref mut check_result) => {
|
||||||
|
result.push(CheckNotice::Info {
|
||||||
|
description: format!("Prüfe Eintrag '{}'", zip_file.name()),
|
||||||
|
line: None,
|
||||||
|
});
|
||||||
|
if check_result.is_empty() {
|
||||||
|
result.push(CheckNotice::Ok(format!(
|
||||||
|
"Keine Probleme in '{}' erkannt",
|
||||||
|
zip_file.name()
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
result.append(check_result)
|
||||||
|
}
|
||||||
|
Err(_) => result.push(CheckNotice::Warning {
|
||||||
|
description: format!(
|
||||||
|
"Überspringe Eintrag '{}': Inhalt kann nicht geprüft werden",
|
||||||
|
zip_file.name(),
|
||||||
|
),
|
||||||
|
line: None,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if zip_file.is_file() {
|
||||||
|
result.push(CheckNotice::Warning {
|
||||||
|
description: format!(
|
||||||
|
"Überspringe Eintrag '{}': Keine OSC-Datei",
|
||||||
|
zip_file.name()
|
||||||
|
),
|
||||||
|
line: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(CheckNotice::Error {
|
||||||
|
description: "Kann Datei nicht lesen".to_string(),
|
||||||
|
line: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
78
src/checks/osc.rs
Normal file
78
src/checks/osc.rs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::checks::{CheckNotice, Checkable};
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
|
||||||
|
pub fn check_file(file: &Path) -> Result<Vec<CheckNotice>, CheckNotice> {
|
||||||
|
match fs::read_to_string(file) {
|
||||||
|
Ok(content) => check(content),
|
||||||
|
_ => Err(CheckNotice::Error {
|
||||||
|
description: "Kann Datei nicht lesen".to_string(),
|
||||||
|
line: None,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(content: String) -> Result<Vec<CheckNotice>, CheckNotice> {
|
||||||
|
let mut result = content
|
||||||
|
.lines()
|
||||||
|
.enumerate()
|
||||||
|
.flat_map(|(line, content)| check_line(line, content.to_string()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let inner_checks = &mut match OnkostarEditor::from_str(content.as_str()) {
|
||||||
|
Ok(data) => data.check(),
|
||||||
|
Err(err) => {
|
||||||
|
return Err(CheckNotice::Error {
|
||||||
|
description: format!("Interner Fehler: {}", err),
|
||||||
|
line: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
result.append(inner_checks);
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_line(line: usize, content: String) -> Vec<CheckNotice> {
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
if content.contains(" </Bezeichnung>") {
|
||||||
|
result.append(&mut vec![CheckNotice::ErrorWithCode {
|
||||||
|
code: "2023-0003".to_string(),
|
||||||
|
description:
|
||||||
|
"Leerzeichen am Ende der Plausibilitätsregel-Bezeichnung (OSTARSUPP-13334)"
|
||||||
|
.to_string(),
|
||||||
|
line: Some(line),
|
||||||
|
example: Some(content.trim().to_string()),
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
89
src/cli.rs
89
src/cli.rs
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -22,18 +22,28 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Command, CommandFactory, Parser, Subcommand};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn build_cli() -> Command {
|
||||||
|
Cli::command()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(author, version, about, long_about = None)]
|
#[command(author, version, about)]
|
||||||
#[command(propagate_version = true, arg_required_else_help(true))]
|
#[command(arg_required_else_help(true))]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Command,
|
pub cmd: SubCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum Command {
|
pub enum SubCommand {
|
||||||
|
#[command(
|
||||||
|
name = "sha256sum",
|
||||||
|
about = "Berechne SHA256 Prüfsumme für die angegebene Datei"
|
||||||
|
)]
|
||||||
|
Sha256Sum { inputfile: String },
|
||||||
#[command(about = "Zeigt alle enthaltenen Kataloge und Formulare mit Revision an.")]
|
#[command(about = "Zeigt alle enthaltenen Kataloge und Formulare mit Revision an.")]
|
||||||
List {
|
List {
|
||||||
inputfile: String,
|
inputfile: String,
|
||||||
@@ -42,6 +52,19 @@ pub enum Command {
|
|||||||
help = "Sortiere Kataloge und Formulare nach Name (Optional)"
|
help = "Sortiere Kataloge und Formulare nach Name (Optional)"
|
||||||
)]
|
)]
|
||||||
sorted: bool,
|
sorted: bool,
|
||||||
|
#[arg(long = "filter", help = "Filtere Ausgabe nach Name (Optional)")]
|
||||||
|
filter: Option<String>,
|
||||||
|
},
|
||||||
|
#[command(about = "Zeigt Kataloge und Formulare mit Revision und Abhängigkeiten an.")]
|
||||||
|
Tree {
|
||||||
|
inputfile: String,
|
||||||
|
#[arg(
|
||||||
|
long = "sorted",
|
||||||
|
help = "Sortiere Kataloge und Formulare nach Name (Optional)"
|
||||||
|
)]
|
||||||
|
sorted: bool,
|
||||||
|
#[arg(long = "filter", help = "Filtere Ausgabe nach Name (Optional)")]
|
||||||
|
filter: Option<String>,
|
||||||
},
|
},
|
||||||
#[command(about = "Modifiziert die angegebene Datei anhand der Profildatei")]
|
#[command(about = "Modifiziert die angegebene Datei anhand der Profildatei")]
|
||||||
Modify {
|
Modify {
|
||||||
@@ -53,10 +76,28 @@ pub enum Command {
|
|||||||
#[arg(long = "compact", help = "Kompakte Ausgabe, ohne Einrücken (Optional)")]
|
#[arg(long = "compact", help = "Kompakte Ausgabe, ohne Einrücken (Optional)")]
|
||||||
compact: bool,
|
compact: bool,
|
||||||
#[arg(
|
#[arg(
|
||||||
long = "x-sorted",
|
long = "sorted",
|
||||||
help = "EXPERIMENTELL: Sortiere Kataloge und Formulare nach Name (Optional). Kann negative Auswirkungen auf den ordnungsgemäßen Import haben."
|
alias = "x-sorted",
|
||||||
|
help = "Sortiere Kataloge und Formulare nach Name und Abhängigkeiten (Optional)."
|
||||||
)]
|
)]
|
||||||
sorted: bool,
|
sorted: bool,
|
||||||
|
#[arg(
|
||||||
|
long = "strip",
|
||||||
|
alias = "x-strip",
|
||||||
|
help = "Entferne Einträge aus der Systembibliothek die nicht importiert werden (Optional)."
|
||||||
|
)]
|
||||||
|
strip: bool,
|
||||||
|
#[arg(
|
||||||
|
short = 'i',
|
||||||
|
long = "interactive",
|
||||||
|
help = "Starte interaktiven Dialog zum Modifizieren von OSC-Dateien"
|
||||||
|
)]
|
||||||
|
interactive: bool,
|
||||||
|
#[arg(
|
||||||
|
long = "fix",
|
||||||
|
help = "Erweiterte Problembehandlung und Reparatur der OSC-Datei"
|
||||||
|
)]
|
||||||
|
fix: bool,
|
||||||
},
|
},
|
||||||
#[command(about = "Vergleiche zwei Dateien anhand der Revision der enthaltenen Inhalte")]
|
#[command(about = "Vergleiche zwei Dateien anhand der Revision der enthaltenen Inhalte")]
|
||||||
Diff {
|
Diff {
|
||||||
@@ -65,4 +106,36 @@ pub enum Command {
|
|||||||
#[arg(long = "strict", help = "Strikter Vergleich des Inhalts")]
|
#[arg(long = "strict", help = "Strikter Vergleich des Inhalts")]
|
||||||
strict: bool,
|
strict: bool,
|
||||||
},
|
},
|
||||||
|
#[command(about = if cfg!(feature = "unzip-osb") { "Prüfe eine OSB- oder OSC-Datei auf bekannte Problemen" } else { "Prüfe eine OSC-Datei auf bekannte Problemen" })]
|
||||||
|
Check {
|
||||||
|
#[arg(help = "Die zu prüfende Datei", group = "check-file", required = true)]
|
||||||
|
file: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
short = 'p',
|
||||||
|
long = "password",
|
||||||
|
help = "Passwort der OSB-Datei (Optional - für OSB-Dateien)",
|
||||||
|
requires = "check-file",
|
||||||
|
hide = !cfg!(feature = "unzip-osb")
|
||||||
|
)]
|
||||||
|
password: Option<String>,
|
||||||
|
#[arg(
|
||||||
|
long = "list",
|
||||||
|
help = "Prüfe nicht und zeige Liste mit Checks auf bekannte Problemen",
|
||||||
|
conflicts_with = "check-file"
|
||||||
|
)]
|
||||||
|
list: bool,
|
||||||
|
},
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
#[command(about = "Entpackt eine OSB-Datei")]
|
||||||
|
UnzipOsb {
|
||||||
|
file: String,
|
||||||
|
#[arg(
|
||||||
|
short = 'p',
|
||||||
|
long = "password",
|
||||||
|
help = "Passwort der OSB-Datei (Optional)"
|
||||||
|
)]
|
||||||
|
password: Option<String>,
|
||||||
|
#[arg(short = 'd', help = "Zielverzeichnis (Optional)")]
|
||||||
|
dir: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
316
src/commands.rs
Normal file
316
src/commands.rs
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::ops::Add;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
use dialoguer::Confirm;
|
||||||
|
use quick_xml::se::Serializer;
|
||||||
|
use serde::Serialize;
|
||||||
|
use sha256::digest;
|
||||||
|
|
||||||
|
use crate::checks::{check_file, print_checks, CheckNotice};
|
||||||
|
use crate::cli::SubCommand;
|
||||||
|
use crate::file_io::{FileError, FileReader, InputFile};
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::profile::Profile;
|
||||||
|
|
||||||
|
fn write_outputfile(filename: String, content: &String) -> Result<(), FileError> {
|
||||||
|
OpenOptions::new()
|
||||||
|
.read(false)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(filename.clone())
|
||||||
|
.map_err(|err| FileError::Writing(filename.clone(), err.to_string()))?
|
||||||
|
.write_all(content.as_bytes())
|
||||||
|
.map_err(|err| FileError::Writing(filename, err.to_string()))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(command: SubCommand) -> Result<(), Box<dyn Error>> {
|
||||||
|
match command {
|
||||||
|
SubCommand::List {
|
||||||
|
inputfile,
|
||||||
|
sorted,
|
||||||
|
filter,
|
||||||
|
} => match InputFile::read(inputfile, None)? {
|
||||||
|
osc @ InputFile::Osc { .. } => {
|
||||||
|
let mut content: OnkostarEditor = osc.try_into()?;
|
||||||
|
if sorted {
|
||||||
|
content.sorted()
|
||||||
|
}
|
||||||
|
if let Some(name) = filter {
|
||||||
|
OnkostarEditor::print_list_filtered(&mut content, name.as_str());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
content.print_list();
|
||||||
|
}
|
||||||
|
InputFile::Osb { content, .. } => {
|
||||||
|
for file in content {
|
||||||
|
match file {
|
||||||
|
InputFile::Osc { .. } => {
|
||||||
|
println!(
|
||||||
|
"{}{}",
|
||||||
|
style("OSB-Paketinhalt: ").bold().yellow(),
|
||||||
|
style(file.filename()).bold()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut content: OnkostarEditor = match file.try_into() {
|
||||||
|
Ok(oe) => oe,
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if sorted {
|
||||||
|
content.sorted()
|
||||||
|
}
|
||||||
|
if let Some(name) = filter {
|
||||||
|
OnkostarEditor::print_list_filtered(&mut content, name.as_str());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
content.print_list();
|
||||||
|
println!()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"{}{}{}",
|
||||||
|
style("OSB-Paketinhalt: ").bold().yellow(),
|
||||||
|
style(file.filename()).bold(),
|
||||||
|
style(" ignoriert").yellow()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
InputFile::Yaml { filename, .. } | InputFile::Other { filename, .. } => {
|
||||||
|
return Err(Box::new(FileError::Reading(
|
||||||
|
filename,
|
||||||
|
"Nur OSB- und OSC-Dateien werden unterstützt".to_string(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SubCommand::Tree {
|
||||||
|
inputfile,
|
||||||
|
sorted,
|
||||||
|
filter,
|
||||||
|
} => match InputFile::read(inputfile, None)? {
|
||||||
|
osc @ InputFile::Osc { .. } => {
|
||||||
|
let mut content: OnkostarEditor = osc.try_into()?;
|
||||||
|
if sorted {
|
||||||
|
content.sorted()
|
||||||
|
}
|
||||||
|
if let Some(name) = filter {
|
||||||
|
OnkostarEditor::print_tree_filtered(&mut content, name.as_str());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
OnkostarEditor::print_tree(&content);
|
||||||
|
}
|
||||||
|
InputFile::Osb { filename, .. } => return Err(Box::new(FileError::Reading(
|
||||||
|
filename,
|
||||||
|
"Nur OSC-Dateien werden unterstützt. OSB-Dateien erzeugen eine zu lange Ausgabe."
|
||||||
|
.to_string(),
|
||||||
|
))),
|
||||||
|
InputFile::Yaml { filename, .. } | InputFile::Other { filename, .. } => {
|
||||||
|
return Err(Box::new(FileError::Reading(
|
||||||
|
filename,
|
||||||
|
"Nur OSC-Dateien werden unterstützt".to_string(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SubCommand::Modify {
|
||||||
|
inputfile,
|
||||||
|
profile,
|
||||||
|
outputfile,
|
||||||
|
compact,
|
||||||
|
sorted,
|
||||||
|
strip,
|
||||||
|
interactive,
|
||||||
|
fix,
|
||||||
|
} => {
|
||||||
|
let mut data: OnkostarEditor = InputFile::read(inputfile, None)?.try_into()?;
|
||||||
|
|
||||||
|
if let Some(profile) = profile {
|
||||||
|
let profile = if profile.contains('.') {
|
||||||
|
FileReader::<Profile>::read(profile)?
|
||||||
|
} else {
|
||||||
|
Profile::embedded_profile(profile.as_str())?
|
||||||
|
};
|
||||||
|
|
||||||
|
data.apply_profile(&profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut compact = compact;
|
||||||
|
let mut sorted = sorted;
|
||||||
|
let mut strip = strip;
|
||||||
|
|
||||||
|
if interactive {
|
||||||
|
compact = Confirm::new()
|
||||||
|
.with_prompt("Kompakte Ausgabe, ohne Einrücken?")
|
||||||
|
.default(compact)
|
||||||
|
.interact()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
sorted = Confirm::new()
|
||||||
|
.with_prompt("Sortiere Kataloge und Formulare nach Name und Abhängigkeiten?")
|
||||||
|
.default(sorted)
|
||||||
|
.interact()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
strip = Confirm::new()
|
||||||
|
.with_prompt(
|
||||||
|
"Entferne Einträge aus der Systembibliothek die nicht importiert werden?",
|
||||||
|
)
|
||||||
|
.default(strip)
|
||||||
|
.interact()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if fix {
|
||||||
|
// No operation as of now
|
||||||
|
}
|
||||||
|
|
||||||
|
if sorted {
|
||||||
|
data.sorted();
|
||||||
|
}
|
||||||
|
|
||||||
|
if strip {
|
||||||
|
data.strip_system_library_content();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
let mut serializer = Serializer::new(&mut buf);
|
||||||
|
if !compact {
|
||||||
|
serializer.indent(' ', 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.serialize(serializer).expect("Generated XML");
|
||||||
|
|
||||||
|
let output = &"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||||
|
.to_string()
|
||||||
|
.add(
|
||||||
|
buf
|
||||||
|
// Replace ' and " as used in original file
|
||||||
|
.replace("'", "'")
|
||||||
|
.replace(""", "\"")
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
|
||||||
|
match outputfile {
|
||||||
|
Some(filename) => write_outputfile(filename, output)?,
|
||||||
|
None => {
|
||||||
|
println!("{}", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SubCommand::Diff {
|
||||||
|
inputfile_a,
|
||||||
|
inputfile_b,
|
||||||
|
strict,
|
||||||
|
} => {
|
||||||
|
println!(
|
||||||
|
"Vergleiche Datei A ({}) mit Datei B ({})",
|
||||||
|
style(&inputfile_a).yellow(),
|
||||||
|
style(&inputfile_b).yellow()
|
||||||
|
);
|
||||||
|
|
||||||
|
let data_a = &mut FileReader::<OnkostarEditor>::read(inputfile_a)?;
|
||||||
|
let data_b = &mut FileReader::<OnkostarEditor>::read(inputfile_b)?;
|
||||||
|
|
||||||
|
data_a.print_diff(data_b, strict);
|
||||||
|
}
|
||||||
|
SubCommand::Sha256Sum { inputfile } => match fs::read_to_string(inputfile.clone()) {
|
||||||
|
Ok(content) => {
|
||||||
|
println!(
|
||||||
|
"{} {}",
|
||||||
|
digest(content).as_str(),
|
||||||
|
PathBuf::from(inputfile.clone())
|
||||||
|
.canonicalize()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or_default()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("{}", FileError::Reading(inputfile, err.to_string()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SubCommand::Check {
|
||||||
|
file,
|
||||||
|
list,
|
||||||
|
password,
|
||||||
|
} => {
|
||||||
|
if list {
|
||||||
|
print_checks();
|
||||||
|
} else {
|
||||||
|
match check_file(Path::new(file.unwrap_or_default().as_str()), password) {
|
||||||
|
Ok(notices) => {
|
||||||
|
println!(
|
||||||
|
"Es wurden {} Probleme gefunden\n",
|
||||||
|
notices
|
||||||
|
.iter()
|
||||||
|
.filter(|notice| matches!(
|
||||||
|
notice,
|
||||||
|
CheckNotice::ErrorWithCode { .. } | CheckNotice::Error { .. }
|
||||||
|
))
|
||||||
|
.count()
|
||||||
|
);
|
||||||
|
notices
|
||||||
|
.iter()
|
||||||
|
.for_each(|check_notice| println!("{}", check_notice));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("{}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
SubCommand::UnzipOsb {
|
||||||
|
file,
|
||||||
|
password,
|
||||||
|
dir,
|
||||||
|
} => {
|
||||||
|
use crate::unzip_osb::{unzip_osb, unzip_osb_using_password};
|
||||||
|
match password {
|
||||||
|
Some(password) => unzip_osb_using_password(
|
||||||
|
file.as_str(),
|
||||||
|
dir.unwrap_or_default().as_str(),
|
||||||
|
password.as_str(),
|
||||||
|
),
|
||||||
|
None => unzip_osb(file.as_str(), dir.unwrap_or_default().as_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
226
src/file_io.rs
Normal file
226
src/file_io.rs
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::fs;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::profile::Profile;
|
||||||
|
|
||||||
|
pub enum FileError {
|
||||||
|
Reading(String, String),
|
||||||
|
Writing(String, String),
|
||||||
|
Parsing(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for FileError {}
|
||||||
|
|
||||||
|
impl Debug for FileError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FileError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match &self {
|
||||||
|
FileError::Reading(filename, err) => format!("Kann Datei '{}' nicht lesen: {}", filename, err),
|
||||||
|
FileError::Writing(filename, err) => format!("Kann Datei '{}' nicht schreiben: {}", filename, err),
|
||||||
|
FileError::Parsing(filename, err) => format!(
|
||||||
|
"Die Datei '{}' wird nicht unterstützt, ist fehlerhaft oder enthält zusätzliche Inhalte\n{}",
|
||||||
|
filename,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum InputFile {
|
||||||
|
Osc {
|
||||||
|
filename: String,
|
||||||
|
content: String,
|
||||||
|
},
|
||||||
|
Osb {
|
||||||
|
filename: String,
|
||||||
|
content: Vec<InputFile>,
|
||||||
|
},
|
||||||
|
Yaml {
|
||||||
|
filename: String,
|
||||||
|
content: String,
|
||||||
|
},
|
||||||
|
Other {
|
||||||
|
filename: String,
|
||||||
|
content: Vec<u8>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputFile {
|
||||||
|
pub fn filename(&self) -> String {
|
||||||
|
match self {
|
||||||
|
InputFile::Osc { filename, .. } => filename,
|
||||||
|
InputFile::Osb { filename, .. } => filename,
|
||||||
|
InputFile::Yaml { filename, .. } => filename,
|
||||||
|
InputFile::Other { filename, .. } => filename,
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(filename: String, _password: Option<String>) -> Result<Self, FileError> {
|
||||||
|
if let Some(extension) = Path::new(filename.as_str()).extension() {
|
||||||
|
return match extension.to_str() {
|
||||||
|
Some("osc") => match fs::read_to_string(filename.clone()) {
|
||||||
|
Ok(content) => Ok(InputFile::Osc { filename, content }),
|
||||||
|
Err(err) => Err(FileError::Reading(filename, err.to_string())),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
|
Some("osb") => {
|
||||||
|
use bytes::BytesMut;
|
||||||
|
use deob::deobfuscate;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
let file = match fs::File::open(filename.clone()) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => return Err(FileError::Reading(filename, err.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut archive = match zip::ZipArchive::new(file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => return Err(FileError::Reading(filename, err.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = vec![];
|
||||||
|
|
||||||
|
let password = _password.unwrap_or_else(|| deobfuscate(env!("OSB_KEY").trim()));
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
if let Ok(Ok(mut zip_file)) =
|
||||||
|
archive.by_index_decrypt(i, password.as_bytes())
|
||||||
|
{
|
||||||
|
if zip_file.is_file() && zip_file.name().ends_with(".osc") {
|
||||||
|
let mut buf = String::new();
|
||||||
|
let _ = zip_file.read_to_string(&mut buf);
|
||||||
|
result.push(InputFile::Osc {
|
||||||
|
filename: zip_file.name().to_string(),
|
||||||
|
content: buf,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
let _ = zip_file.read(&mut buf);
|
||||||
|
result.push(InputFile::Other {
|
||||||
|
filename: zip_file.name().to_string(),
|
||||||
|
content: buf.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(FileError::Parsing(
|
||||||
|
filename.into(),
|
||||||
|
"Kann OSB-Datei nicht lesen".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(InputFile::Osb {
|
||||||
|
filename,
|
||||||
|
content: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Some("yml") | Some("yaml") => match fs::read_to_string(filename.clone()) {
|
||||||
|
Ok(content) => Ok(InputFile::Yaml { filename, content }),
|
||||||
|
Err(err) => Err(FileError::Reading(filename, err.to_string())),
|
||||||
|
},
|
||||||
|
_ => Err(FileError::Parsing(
|
||||||
|
filename,
|
||||||
|
"Kein unterstütztes Dateiformat".to_string(),
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(FileError::Reading(filename, String::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<InputFile> for OnkostarEditor {
|
||||||
|
type Error = FileError;
|
||||||
|
|
||||||
|
fn try_from(value: InputFile) -> Result<Self, Self::Error> {
|
||||||
|
return match value {
|
||||||
|
InputFile::Osc {
|
||||||
|
filename, content, ..
|
||||||
|
} => match OnkostarEditor::from_str(content.as_str()) {
|
||||||
|
Ok(data) => Ok(data),
|
||||||
|
Err(err) => Err(FileError::Parsing(filename, err)),
|
||||||
|
},
|
||||||
|
InputFile::Osb { filename, .. }
|
||||||
|
| InputFile::Yaml { filename, .. }
|
||||||
|
| InputFile::Other { filename, .. } => {
|
||||||
|
Err(FileError::Parsing(filename, "Keine OSC-Datei".to_string()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<InputFile> for Profile {
|
||||||
|
type Error = FileError;
|
||||||
|
|
||||||
|
fn try_from(value: InputFile) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
InputFile::Yaml { filename, content } => match Profile::from_str(&content) {
|
||||||
|
Ok(profile) => Ok(profile),
|
||||||
|
Err(err) => Err(FileError::Parsing(filename, err)),
|
||||||
|
},
|
||||||
|
InputFile::Osc { filename, .. }
|
||||||
|
| InputFile::Osb { filename, .. }
|
||||||
|
| InputFile::Other { filename, .. } => Err(FileError::Parsing(
|
||||||
|
filename,
|
||||||
|
"Keine Profildatei".to_string(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shortcut methods for OSC and Profile files
|
||||||
|
pub struct FileReader<FileType> {
|
||||||
|
file_type: PhantomData<FileType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileReader<OnkostarEditor> {
|
||||||
|
pub fn read(filename: String) -> Result<OnkostarEditor, FileError> {
|
||||||
|
TryInto::<OnkostarEditor>::try_into(InputFile::read(filename.to_string(), None)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileReader<Profile> {
|
||||||
|
pub fn read(filename: String) -> Result<Profile, FileError> {
|
||||||
|
TryInto::<Profile>::try_into(InputFile::read(filename.to_string(), None)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
160
src/main.rs
160
src/main.rs
@@ -22,164 +22,22 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::error::Error;
|
use crate::cli::Cli;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use crate::commands::handle;
|
||||||
use std::fs;
|
|
||||||
use std::fs::OpenOptions;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::ops::Add;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use console::style;
|
use std::error::Error;
|
||||||
use quick_xml::se::Serializer;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::cli::{Cli, Command};
|
|
||||||
use crate::model::onkostar_editor::OnkostarEditor;
|
|
||||||
use crate::profile::Profile;
|
|
||||||
|
|
||||||
|
mod checks;
|
||||||
mod cli;
|
mod cli;
|
||||||
|
mod commands;
|
||||||
|
mod file_io;
|
||||||
mod model;
|
mod model;
|
||||||
mod profile;
|
mod profile;
|
||||||
|
#[cfg(feature = "unzip-osb")]
|
||||||
enum FileError {
|
mod unzip_osb;
|
||||||
Reading(String, String),
|
|
||||||
Writing(String, String),
|
|
||||||
Parsing(String, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for FileError {}
|
|
||||||
|
|
||||||
impl Debug for FileError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Display::fmt(self, f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FileError {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
match &self {
|
|
||||||
FileError::Reading(filename, err) => format!("Kann Datei '{}' nicht lesen: {}", filename, err),
|
|
||||||
FileError::Writing(filename, err) => format!("Kann Datei '{}' nicht schreiben: {}", filename, err),
|
|
||||||
FileError::Parsing(filename, err) => format!(
|
|
||||||
"Die Datei '{}' ist entweder keine OSC-Datei, fehlerhaft oder enthält zusätzliche Inhalte\n{}",
|
|
||||||
filename,
|
|
||||||
err
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_inputfile(inputfile: String) -> Result<OnkostarEditor, FileError> {
|
|
||||||
return match fs::read_to_string(inputfile.clone()) {
|
|
||||||
Ok(content) => match OnkostarEditor::from_str(content.as_str()) {
|
|
||||||
Ok(data) => Ok(data),
|
|
||||||
Err(err) => Err(FileError::Parsing(inputfile, err)),
|
|
||||||
},
|
|
||||||
Err(err) => Err(FileError::Reading(inputfile, err.to_string())),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_outputfile(filename: String, content: &String) -> Result<(), FileError> {
|
|
||||||
OpenOptions::new()
|
|
||||||
.read(false)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(filename.clone())
|
|
||||||
.map_err(|err| FileError::Writing(filename.clone(), err.to_string()))?
|
|
||||||
.write_all(content.as_bytes())
|
|
||||||
.map_err(|err| FileError::Writing(filename, err.to_string()))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_profile(filename: String) -> Result<Profile, FileError> {
|
|
||||||
let profile = fs::read_to_string(filename.clone())
|
|
||||||
.map_err(|err| FileError::Reading(filename.clone(), err.to_string()))?;
|
|
||||||
let profile =
|
|
||||||
Profile::from_str(profile.as_str()).map_err(|err| FileError::Reading(filename, err))?;
|
|
||||||
Ok(profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
handle(cli.cmd)?;
|
||||||
match cli.command {
|
|
||||||
Command::List { inputfile, sorted } => {
|
|
||||||
let mut data = read_inputfile(inputfile)?;
|
|
||||||
if sorted {
|
|
||||||
data.sorted()
|
|
||||||
}
|
|
||||||
data.print_list();
|
|
||||||
}
|
|
||||||
Command::Modify {
|
|
||||||
inputfile,
|
|
||||||
profile,
|
|
||||||
outputfile,
|
|
||||||
compact,
|
|
||||||
sorted,
|
|
||||||
} => {
|
|
||||||
let data = &mut read_inputfile(inputfile)?;
|
|
||||||
|
|
||||||
if let Some(profile) = profile {
|
|
||||||
let profile = read_profile(profile.clone()).map_err(|_| {
|
|
||||||
FileError::Reading(profile, "Kann Profildatei nicht lesen!".into())
|
|
||||||
})?;
|
|
||||||
data.apply_profile(&profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if sorted {
|
|
||||||
data.sorted();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = String::new();
|
|
||||||
|
|
||||||
let mut serializer = Serializer::new(&mut buf);
|
|
||||||
if !compact {
|
|
||||||
serializer.indent(' ', 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.serialize(serializer).expect("Generated XML");
|
|
||||||
|
|
||||||
let output = &"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
||||||
.to_string()
|
|
||||||
.add(
|
|
||||||
buf
|
|
||||||
// Replace ' and " as used in original file
|
|
||||||
.replace("'", "'")
|
|
||||||
.replace(""", "\"")
|
|
||||||
.as_str(),
|
|
||||||
);
|
|
||||||
|
|
||||||
match outputfile {
|
|
||||||
Some(filename) => write_outputfile(filename, output)?,
|
|
||||||
None => {
|
|
||||||
println!("{}", output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Command::Diff {
|
|
||||||
inputfile_a,
|
|
||||||
inputfile_b,
|
|
||||||
strict,
|
|
||||||
} => {
|
|
||||||
println!(
|
|
||||||
"Vergleiche Datei A ({}) mit Datei B ({})",
|
|
||||||
style(&inputfile_a).yellow(),
|
|
||||||
style(&inputfile_b).yellow()
|
|
||||||
);
|
|
||||||
|
|
||||||
let data_a = &mut read_inputfile(inputfile_a)?;
|
|
||||||
let data_b = &mut read_inputfile(inputfile_b)?;
|
|
||||||
|
|
||||||
data_a.print_diff(data_b, strict);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,14 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::model::{Comparable, Listable, Ordner, Sortable};
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::model::requirements::{Requirement, Requires};
|
||||||
|
use crate::model::{Ansichten, Comparable, FolderContent, Listable, Ordner, Sortable};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
@@ -54,12 +58,19 @@ pub struct DataCatalogue {
|
|||||||
entries: Entries,
|
entries: Entries,
|
||||||
#[serde(rename = "Ordner")]
|
#[serde(rename = "Ordner")]
|
||||||
ordner: Ordner,
|
ordner: Ordner,
|
||||||
|
#[serde(rename = "Ansichten", default)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
ansichten: Option<Ansichten>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Listable for DataCatalogue {
|
impl Listable for DataCatalogue {
|
||||||
fn to_listed_string(&self) -> String {
|
fn to_listed_string(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Datenkatalog '{}' in Revision '{}'",
|
"Datenkatalog ({}) '{}' in Revision '{}'",
|
||||||
|
match self.is_system_library_content() {
|
||||||
|
true => style("S").yellow(),
|
||||||
|
_ => style("u"),
|
||||||
|
},
|
||||||
style(&self.name).yellow(),
|
style(&self.name).yellow(),
|
||||||
style(&self.revision).yellow()
|
style(&self.revision).yellow()
|
||||||
)
|
)
|
||||||
@@ -92,6 +103,58 @@ impl Comparable for DataCatalogue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Requires for DataCatalogue {
|
||||||
|
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
|
||||||
|
let mut result = self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|&entry| entry.property_catalogue.is_some())
|
||||||
|
.map(|entry| match &entry.property_catalogue {
|
||||||
|
Some(entry) => entry.to_string(),
|
||||||
|
_ => String::new(),
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_property_catalogue(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::PropertyCatalogue(contained),
|
||||||
|
None => Requirement::ExternalPropertyCatalogue(entry),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
result.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String {
|
||||||
|
format!(
|
||||||
|
"{}\n{}",
|
||||||
|
self.to_listed_string(),
|
||||||
|
self.get_required_entries(all)
|
||||||
|
.iter()
|
||||||
|
.map(|entry| match entry {
|
||||||
|
Requirement::PropertyCatalogue(_) => {
|
||||||
|
Some(format!(" - {}\n", entry))
|
||||||
|
}
|
||||||
|
Requirement::ExternalPropertyCatalogue(_) => {
|
||||||
|
Some(format!(" - {}\n", entry))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter(Option::is_some)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderContent for DataCatalogue {
|
||||||
|
fn get_library_folder(&self) -> String {
|
||||||
|
self.ordner.bibliothek.name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Entries {
|
pub struct Entries {
|
||||||
|
|||||||
@@ -22,12 +22,20 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::checks::CheckNotice::ErrorWithCode;
|
||||||
|
use crate::checks::{CheckNotice, Checkable};
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::model::requirements::{Requirement, Requires};
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
|
apply_profile_to_form_entry, apply_profile_to_form_field, Ansichten, Comparable, Entries,
|
||||||
FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
|
Filter, FolderContent, FormEntry, FormEntryContainer, Kennzahlen, Listable, MenuCategory,
|
||||||
|
PlausibilityRules, PunkteKategorien, RefEntries, Script, Sortable,
|
||||||
};
|
};
|
||||||
use crate::model::{Haeufigkeiten, Ordner};
|
use crate::model::{Haeufigkeiten, Ordner};
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
@@ -74,18 +82,26 @@ pub struct DataForm {
|
|||||||
#[serde(rename = "EmailTemplate")]
|
#[serde(rename = "EmailTemplate")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
mail_template: Option<String>,
|
mail_template: Option<String>,
|
||||||
#[serde(rename = "ErkrankungText")]
|
#[serde(rename = "ErkrankungText", default)]
|
||||||
erkrankung_text: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
erkrankung_text: Option<String>,
|
||||||
#[serde(rename = "ErkrankungTextLong")]
|
#[serde(rename = "ErkrankungTextLong")]
|
||||||
erkrankung_text_long: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
erkrankung_text_long: Option<String>,
|
||||||
#[serde(rename = "ErkrankungProzedurText")]
|
#[serde(rename = "ErkrankungProzedurText")]
|
||||||
erkrankung_prozedur_text: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
erkrankung_prozedur_text: Option<String>,
|
||||||
#[serde(rename = "ErkrankungSummary")]
|
#[serde(rename = "ErkrankungSummary")]
|
||||||
erkrankung_summary: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
erkrankung_summary: Option<String>,
|
||||||
#[serde(rename = "ErkrankungBigSummary")]
|
#[serde(rename = "ErkrankungBigSummary")]
|
||||||
erkrankung_big_summary: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
erkrankung_big_summary: Option<String>,
|
||||||
#[serde(rename = "Kontext")]
|
#[serde(rename = "Kontext")]
|
||||||
kontext: String,
|
kontext: String,
|
||||||
|
#[serde(rename = "Datenart")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
datenart: Option<String>,
|
||||||
#[serde(rename = "TudokReadonly")]
|
#[serde(rename = "TudokReadonly")]
|
||||||
tudok_readonly: bool,
|
tudok_readonly: bool,
|
||||||
#[serde(rename = "VitalstatusRelevant")]
|
#[serde(rename = "VitalstatusRelevant")]
|
||||||
@@ -132,6 +148,9 @@ pub struct DataForm {
|
|||||||
guid: String,
|
guid: String,
|
||||||
#[serde(rename = "Revision")]
|
#[serde(rename = "Revision")]
|
||||||
revision: u16,
|
revision: u16,
|
||||||
|
#[serde(rename = "VerknuepftGUID")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
verknuepft_guid: Option<String>,
|
||||||
#[serde(rename = "SeitenzahlSichtbar")]
|
#[serde(rename = "SeitenzahlSichtbar")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
seitenanzahl_sichtbar: Option<bool>,
|
seitenanzahl_sichtbar: Option<bool>,
|
||||||
@@ -142,12 +161,15 @@ pub struct DataForm {
|
|||||||
#[serde(rename = "Haeufigkeiten")]
|
#[serde(rename = "Haeufigkeiten")]
|
||||||
haeufigkeiten: Haeufigkeiten,
|
haeufigkeiten: Haeufigkeiten,
|
||||||
#[serde(rename = "Kennzahlen")]
|
#[serde(rename = "Kennzahlen")]
|
||||||
kennzahlen: String,
|
kennzahlen: Kennzahlen,
|
||||||
#[serde(rename = "Ordner")]
|
#[serde(rename = "Ordner")]
|
||||||
ordner: Ordner,
|
ordner: Ordner,
|
||||||
#[serde(rename = "MenuCategory")]
|
#[serde(rename = "MenuCategory")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
menu_category: Option<MenuCategory>,
|
menu_category: Option<MenuCategory>,
|
||||||
|
#[serde(rename = "PunkteKategorien")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
punkte_kategorien: Option<PunkteKategorien>,
|
||||||
#[serde(rename = "Ansichten")]
|
#[serde(rename = "Ansichten")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
ansichten: Option<Ansichten>,
|
ansichten: Option<Ansichten>,
|
||||||
@@ -165,6 +187,12 @@ impl FormEntryContainer for DataForm {
|
|||||||
apply_profile_to_form_entry(entry, form_reference)
|
apply_profile_to_form_entry(entry, form_reference)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Hide form field using filter set to "false" if requested
|
||||||
|
profile_form
|
||||||
|
.form_fields
|
||||||
|
.iter()
|
||||||
|
.for_each(|form_field| apply_profile_to_form_field(entry, form_field));
|
||||||
|
|
||||||
if let Some(menu_category) = &profile_form.menu_category {
|
if let Some(menu_category) = &profile_form.menu_category {
|
||||||
self.menu_category = Some(MenuCategory {
|
self.menu_category = Some(MenuCategory {
|
||||||
name: menu_category.name.clone(),
|
name: menu_category.name.clone(),
|
||||||
@@ -181,7 +209,11 @@ impl FormEntryContainer for DataForm {
|
|||||||
impl Listable for DataForm {
|
impl Listable for DataForm {
|
||||||
fn to_listed_string(&self) -> String {
|
fn to_listed_string(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Formular '{}' in Revision '{}'",
|
"Formular ({}) '{}' in Revision '{}'",
|
||||||
|
match self.is_system_library_content() {
|
||||||
|
true => style("S").yellow(),
|
||||||
|
_ => style("u"),
|
||||||
|
},
|
||||||
style(&self.name).yellow(),
|
style(&self.name).yellow(),
|
||||||
style(&self.revision).yellow()
|
style(&self.revision).yellow()
|
||||||
)
|
)
|
||||||
@@ -225,6 +257,144 @@ impl Comparable for DataForm {
|
|||||||
fn get_revision(&self) -> u16 {
|
fn get_revision(&self) -> u16 {
|
||||||
self.revision
|
self.revision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compare_by_requirement(a: &Self, b: &Self) -> Ordering {
|
||||||
|
if a.get_name() == b.get_name()
|
||||||
|
|| a.is_system_library_content()
|
||||||
|
|| b.is_system_library_content()
|
||||||
|
{
|
||||||
|
return Ordering::Equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.requires_form_reference(&b.get_name()) || a.requires_subform(&b.get_name()) {
|
||||||
|
return Ordering::Greater;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ordering::Less
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Requires for DataForm {
|
||||||
|
fn requires_form_reference(&self, name: &str) -> bool {
|
||||||
|
self.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
item.type_ == "formReference"
|
||||||
|
&& match item.referenced_data_form.as_ref() {
|
||||||
|
Some(refname) => refname == name,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|&it| it)
|
||||||
|
.last()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
fn requires_subform(&self, name: &str) -> bool {
|
||||||
|
self.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
item.type_ == "subform"
|
||||||
|
&& match item.referenced_data_form.as_ref() {
|
||||||
|
Some(refname) => refname == name,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|&it| it)
|
||||||
|
.last()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
|
||||||
|
let mut result = self
|
||||||
|
.data_catalogues
|
||||||
|
.data_catalogue
|
||||||
|
.iter()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_catalogue(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataCatalogue(contained),
|
||||||
|
None => Requirement::ExternalDataCatalogue(entry.to_string()),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
result.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
|
||||||
|
let referenced_forms = &mut self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|&entry| entry.get_type() == "formReference")
|
||||||
|
.filter_map(|entry| match &entry.referenced_data_form {
|
||||||
|
Some(name) => Some(name),
|
||||||
|
None => None,
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_form(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataFormReference(contained),
|
||||||
|
None => match all.find_unterformular(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::UnterformularReference(contained),
|
||||||
|
None => Requirement::ExternalUnterformularReference(entry.to_string()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
referenced_forms.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
result.append(referenced_forms);
|
||||||
|
|
||||||
|
let sub_forms = &mut self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|&entry| entry.get_type() == "subform")
|
||||||
|
.filter_map(|entry| match &entry.referenced_data_form {
|
||||||
|
Some(name) => Some(name),
|
||||||
|
None => None,
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_form(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataFormSubform(contained),
|
||||||
|
None => match all.find_unterformular(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::UnterformularSubform(contained),
|
||||||
|
None => Requirement::ExternalUnterformularSubform(entry.to_string()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
sub_forms.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
result.append(sub_forms);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderContent for DataForm {
|
||||||
|
fn get_library_folder(&self) -> String {
|
||||||
|
self.ordner.bibliothek.name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Checkable for DataForm {
|
||||||
|
fn check(&self) -> Vec<CheckNotice> {
|
||||||
|
if self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.procedure_date_status != "none")
|
||||||
|
.count()
|
||||||
|
== 0
|
||||||
|
{
|
||||||
|
return vec![ErrorWithCode {
|
||||||
|
code: "2023-0002".to_string(),
|
||||||
|
description: format!(
|
||||||
|
"Formular '{}' hat keine Angabe zum Prozedurdatum",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
line: None,
|
||||||
|
example: None,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -295,6 +465,9 @@ pub struct Entry {
|
|||||||
grafik_ausrichtung: Option<String>,
|
grafik_ausrichtung: Option<String>,
|
||||||
#[serde(rename = "Mandatory")]
|
#[serde(rename = "Mandatory")]
|
||||||
mandatory: String,
|
mandatory: String,
|
||||||
|
#[serde(rename = "Datenart", default)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
datenart: Option<String>,
|
||||||
#[serde(rename = "Filter")]
|
#[serde(rename = "Filter")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
filter: Option<Filter>,
|
filter: Option<Filter>,
|
||||||
@@ -314,6 +487,9 @@ pub struct Entry {
|
|||||||
#[serde(rename = "AnzeigeAuswahl")]
|
#[serde(rename = "AnzeigeAuswahl")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
anzeige_auswahl: Option<String>,
|
anzeige_auswahl: Option<String>,
|
||||||
|
#[serde(rename = "Druckvorlage")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
druckvorlage: Option<String>,
|
||||||
#[serde(rename = "VersionFrom")]
|
#[serde(rename = "VersionFrom")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
version_from: Option<String>,
|
version_from: Option<String>,
|
||||||
@@ -321,6 +497,9 @@ pub struct Entry {
|
|||||||
speichern: String,
|
speichern: String,
|
||||||
#[serde(rename = "LeerAusblenden")]
|
#[serde(rename = "LeerAusblenden")]
|
||||||
leer_ausblenden: bool,
|
leer_ausblenden: bool,
|
||||||
|
#[serde(rename = "Inhalt")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
inhalt: Option<String>,
|
||||||
#[serde(rename = "GeschlossenAnzeigen")]
|
#[serde(rename = "GeschlossenAnzeigen")]
|
||||||
geschlossen_anzeigen: bool,
|
geschlossen_anzeigen: bool,
|
||||||
#[serde(rename = "Min")]
|
#[serde(rename = "Min")]
|
||||||
@@ -330,7 +509,8 @@ pub struct Entry {
|
|||||||
#[serde(rename = "InUebersichtAnzeigen")]
|
#[serde(rename = "InUebersichtAnzeigen")]
|
||||||
in_uebersicht_anzeigen: bool,
|
in_uebersicht_anzeigen: bool,
|
||||||
#[serde(rename = "Hinweis")]
|
#[serde(rename = "Hinweis")]
|
||||||
hinweis: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
hinweis: Option<String>,
|
||||||
#[serde(rename = "Vorschlagskategorie")]
|
#[serde(rename = "Vorschlagskategorie")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
vorschlagskategorie: Option<String>,
|
vorschlagskategorie: Option<String>,
|
||||||
@@ -402,12 +582,21 @@ pub struct Entry {
|
|||||||
in_auswertung: bool,
|
in_auswertung: bool,
|
||||||
#[serde(rename = "InAuswertungGraph")]
|
#[serde(rename = "InAuswertungGraph")]
|
||||||
in_auswertung_graph: bool,
|
in_auswertung_graph: bool,
|
||||||
|
#[serde(rename = "FragebogenItemNummer")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
fragebogen_item_nummer: Option<u8>,
|
||||||
|
#[serde(rename = "Score")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
score: Option<String>,
|
||||||
#[serde(rename = "AlignmentPatModul")]
|
#[serde(rename = "AlignmentPatModul")]
|
||||||
alignment_pat_modul: String,
|
alignment_pat_modul: String,
|
||||||
#[serde(rename = "DirectionPatModul")]
|
#[serde(rename = "DirectionPatModul")]
|
||||||
direction_pat_modul: String,
|
direction_pat_modul: String,
|
||||||
#[serde(rename = "SeitenumbruchPatModul")]
|
#[serde(rename = "SeitenumbruchPatModul")]
|
||||||
seitenumbruch_pat_modul: bool,
|
seitenumbruch_pat_modul: bool,
|
||||||
|
#[serde(rename = "Kontaktliste")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
kontaktliste: Option<String>,
|
||||||
#[serde(rename = "MarkierungIgnorieren")]
|
#[serde(rename = "MarkierungIgnorieren")]
|
||||||
markierung_ignorieren: bool,
|
markierung_ignorieren: bool,
|
||||||
#[serde(rename = "SucheArt")]
|
#[serde(rename = "SucheArt")]
|
||||||
@@ -454,6 +643,15 @@ impl FormEntry for Entry {
|
|||||||
valid: true,
|
valid: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hide(&mut self) {
|
||||||
|
self.filter = Some(Filter {
|
||||||
|
condition: "false".into(),
|
||||||
|
valid: true,
|
||||||
|
ref_entries: Some(RefEntries { ref_entry: None }),
|
||||||
|
});
|
||||||
|
self.speichern = "0".into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sortable for Entry {
|
impl Sortable for Entry {
|
||||||
|
|||||||
132
src/model/mod.rs
132
src/model/mod.rs
@@ -22,16 +22,21 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::profile::{FormReference, Profile};
|
use std::cmp::Ordering;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::model::requirements::Requires;
|
||||||
|
use crate::profile::{FormField, FormReference, Profile};
|
||||||
|
|
||||||
pub mod data_catalogue;
|
pub mod data_catalogue;
|
||||||
pub mod data_form;
|
pub mod data_form;
|
||||||
pub mod onkostar_editor;
|
pub mod onkostar_editor;
|
||||||
pub mod property_catalogue;
|
pub mod property_catalogue;
|
||||||
|
pub mod requirements;
|
||||||
pub mod unterformular;
|
pub mod unterformular;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -91,11 +96,15 @@ pub struct Ansicht {
|
|||||||
#[serde(rename = "Konfiguration")]
|
#[serde(rename = "Konfiguration")]
|
||||||
konfiguration: String,
|
konfiguration: String,
|
||||||
#[serde(rename = "DataForm")]
|
#[serde(rename = "DataForm")]
|
||||||
data_form: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
data_form: Option<String>,
|
||||||
#[serde(rename = "DataCatalogue")]
|
#[serde(rename = "DataCatalogue")]
|
||||||
data_catalogue: String,
|
data_catalogue: String,
|
||||||
#[serde(rename = "TypAuswahl")]
|
#[serde(rename = "TypAuswahl")]
|
||||||
typ_auswahl: String,
|
typ_auswahl: String,
|
||||||
|
#[serde(rename = "PersonenstammKontext", default)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
personenstamm_kontext: Option<String>,
|
||||||
#[serde(rename = "Suche")]
|
#[serde(rename = "Suche")]
|
||||||
suche: bool,
|
suche: bool,
|
||||||
#[serde(rename = "SID")]
|
#[serde(rename = "SID")]
|
||||||
@@ -126,6 +135,66 @@ pub struct MenuCategory {
|
|||||||
column: String,
|
column: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct PunkteKategorien {
|
||||||
|
#[serde(rename = "PunkteKategorie", default)]
|
||||||
|
punkte_kategorie: Vec<PunkteKategorie>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct PunkteKategorie {
|
||||||
|
#[serde(rename = "Name")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "Beschreibung")]
|
||||||
|
beschreibung: String,
|
||||||
|
#[serde(rename = "MaxLeerwerte")]
|
||||||
|
max_leerwerte: u16,
|
||||||
|
#[serde(rename = "Berechnung")]
|
||||||
|
berechnung: String,
|
||||||
|
#[serde(rename = "Felder")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
felder: Option<Felder>,
|
||||||
|
#[serde(rename = "Vergleichswerttabellen")]
|
||||||
|
vergleichswerttabellen: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Felder {
|
||||||
|
#[serde(rename = "Feld", default)]
|
||||||
|
feld: Vec<Feld>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Feld {
|
||||||
|
#[serde(rename = "DataFormEntryName")]
|
||||||
|
data_form_entry_name: String,
|
||||||
|
#[serde(rename = "ManuellePunkte")]
|
||||||
|
manuelle_punkte: bool,
|
||||||
|
#[serde(rename = "Werte")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
werte: Option<FeldWerte>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct FeldWerte {
|
||||||
|
#[serde(rename = "Wert", default)]
|
||||||
|
wert: Vec<FeldWert>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct FeldWert {
|
||||||
|
#[serde(rename = "Wert")]
|
||||||
|
wert: String,
|
||||||
|
#[serde(rename = "Punkte")]
|
||||||
|
punkte: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
@@ -184,8 +253,12 @@ pub struct Haeufigkeit {
|
|||||||
taeglich_aktualisieren: bool,
|
taeglich_aktualisieren: bool,
|
||||||
#[serde(rename = "Typ")]
|
#[serde(rename = "Typ")]
|
||||||
typ: String,
|
typ: String,
|
||||||
|
#[serde(rename = "NichtBerechnen")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
nicht_berechnen: Option<String>,
|
||||||
#[serde(rename = "TabellenName")]
|
#[serde(rename = "TabellenName")]
|
||||||
tabellen_name: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
tabellen_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -209,6 +282,32 @@ pub struct Ordner {
|
|||||||
parent_order: Option<Box<Ordner>>,
|
parent_order: Option<Box<Ordner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Kennzahlen {
|
||||||
|
#[serde(rename = "Kennzahl", default)]
|
||||||
|
kennzahl: Vec<Kennzahl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub struct Kennzahl {
|
||||||
|
#[serde(rename = "Name")]
|
||||||
|
name: String,
|
||||||
|
#[serde(rename = "Nummer")]
|
||||||
|
nummer: String,
|
||||||
|
#[serde(rename = "Beschreibung")]
|
||||||
|
beschreibung: String,
|
||||||
|
#[serde(rename = "Notiz")]
|
||||||
|
notiz: String,
|
||||||
|
#[serde(rename = "Vorgabe")]
|
||||||
|
vorgabe: String,
|
||||||
|
#[serde(rename = "Haeufigkeitenzaehler")]
|
||||||
|
haeufigkeitenzaehler: String,
|
||||||
|
#[serde(rename = "Haeufigkeitennenner")]
|
||||||
|
haeufigkeitennenner: String,
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_profile_to_form_entry<E>(entry: &mut E, form_reference: &FormReference)
|
fn apply_profile_to_form_entry<E>(entry: &mut E, form_reference: &FormReference)
|
||||||
where
|
where
|
||||||
E: FormEntry,
|
E: FormEntry,
|
||||||
@@ -229,6 +328,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_profile_to_form_field<E>(entry: &mut E, form_field: &FormField)
|
||||||
|
where
|
||||||
|
E: FormEntry,
|
||||||
|
{
|
||||||
|
if entry.get_name() == form_field.name && form_field.hide {
|
||||||
|
entry.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FormEntryContainer {
|
pub trait FormEntryContainer {
|
||||||
fn apply_profile(&mut self, profile: &Profile);
|
fn apply_profile(&mut self, profile: &Profile);
|
||||||
}
|
}
|
||||||
@@ -239,6 +347,7 @@ pub trait Listable {
|
|||||||
|
|
||||||
pub trait Sortable {
|
pub trait Sortable {
|
||||||
fn sorting_key(&self) -> String;
|
fn sorting_key(&self) -> String;
|
||||||
|
|
||||||
fn sorted(&mut self) -> &Self
|
fn sorted(&mut self) -> &Self
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@@ -255,6 +364,12 @@ pub trait Comparable: Debug {
|
|||||||
format!("{:?}", self).hash(&mut h);
|
format!("{:?}", self).hash(&mut h);
|
||||||
h.finish().to_string()
|
h.finish().to_string()
|
||||||
}
|
}
|
||||||
|
fn compare_by_requirement(_: &Self, _: &Self) -> Ordering
|
||||||
|
where
|
||||||
|
Self: Requires,
|
||||||
|
{
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FormEntry {
|
pub trait FormEntry {
|
||||||
@@ -264,4 +379,13 @@ pub trait FormEntry {
|
|||||||
fn update_anzeige(&mut self, value: String);
|
fn update_anzeige(&mut self, value: String);
|
||||||
fn update_anzeige_auswahl(&mut self, value: String);
|
fn update_anzeige_auswahl(&mut self, value: String);
|
||||||
fn update_scripts_code(&mut self, value: String);
|
fn update_scripts_code(&mut self, value: String);
|
||||||
|
fn hide(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FolderContent {
|
||||||
|
fn get_library_folder(&self) -> String;
|
||||||
|
|
||||||
|
fn is_system_library_content(&self) -> bool {
|
||||||
|
"ONKOSTAR Bibliothek" == self.get_library_folder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,21 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use console::style;
|
|
||||||
use quick_xml::de::from_str;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
use quick_xml::de::from_str;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::checks::{CheckNotice, Checkable};
|
||||||
use crate::model::data_catalogue::DataCatalogue;
|
use crate::model::data_catalogue::DataCatalogue;
|
||||||
use crate::model::data_form::DataForm;
|
use crate::model::data_form::DataForm;
|
||||||
use crate::model::property_catalogue::PropertyCatalogue;
|
use crate::model::property_catalogue::PropertyCatalogue;
|
||||||
|
use crate::model::requirements::{Requirement, Requires};
|
||||||
use crate::model::unterformular::Unterformular;
|
use crate::model::unterformular::Unterformular;
|
||||||
use crate::model::{Comparable, FormEntryContainer, Listable, Sortable};
|
use crate::model::{Comparable, FolderContent, FormEntryContainer, Listable, Sortable};
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -46,13 +49,73 @@ pub struct OnkostarEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OnkostarEditor {
|
impl OnkostarEditor {
|
||||||
|
pub fn find_property_catalogue<'a>(&'a self, name: &str) -> Option<&'a PropertyCatalogue> {
|
||||||
|
match self
|
||||||
|
.editor
|
||||||
|
.property_catalogue
|
||||||
|
.iter()
|
||||||
|
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
|
||||||
|
.nth(0)
|
||||||
|
{
|
||||||
|
Some(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_data_catalogue<'a>(&'a self, name: &str) -> Option<&'a DataCatalogue> {
|
||||||
|
match self
|
||||||
|
.editor
|
||||||
|
.data_catalogue
|
||||||
|
.iter()
|
||||||
|
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
|
||||||
|
.nth(0)
|
||||||
|
{
|
||||||
|
Some(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_data_form<'a>(&'a self, name: &str) -> Option<&'a DataForm> {
|
||||||
|
match self
|
||||||
|
.editor
|
||||||
|
.data_form
|
||||||
|
.iter()
|
||||||
|
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
|
||||||
|
.nth(0)
|
||||||
|
{
|
||||||
|
Some(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_unterformular<'a>(&'a self, name: &str) -> Option<&'a Unterformular> {
|
||||||
|
match self
|
||||||
|
.editor
|
||||||
|
.unterformular
|
||||||
|
.iter()
|
||||||
|
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
|
||||||
|
.nth(0)
|
||||||
|
{
|
||||||
|
Some(x) => Some(x),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_profile(&mut self, profile: &Profile) {
|
pub fn apply_profile(&mut self, profile: &Profile) {
|
||||||
self.editor.data_form.iter_mut().for_each(|data_form| {
|
self.editor
|
||||||
|
.data_form
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|data_form| !data_form.is_system_library_content())
|
||||||
|
.for_each(|data_form| {
|
||||||
data_form.apply_profile(profile);
|
data_form.apply_profile(profile);
|
||||||
});
|
});
|
||||||
self.editor.unterformular.iter_mut().for_each(|data_form| {
|
self.editor
|
||||||
|
.unterformular
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|data_form| !data_form.is_system_library_content())
|
||||||
|
.for_each(|data_form| {
|
||||||
data_form.apply_profile(profile);
|
data_form.apply_profile(profile);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_list(&self) {
|
pub fn print_list(&self) {
|
||||||
@@ -68,12 +131,88 @@ impl OnkostarEditor {
|
|||||||
Self::print_items("Unterformulare", &self.editor.unterformular);
|
Self::print_items("Unterformulare", &self.editor.unterformular);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter_by_name_contains(&mut self, name: &str) {
|
||||||
|
self.editor
|
||||||
|
.property_catalogue
|
||||||
|
.retain(|e| e.get_name().contains(name));
|
||||||
|
self.editor
|
||||||
|
.data_catalogue
|
||||||
|
.retain(|e| e.get_name().contains(name));
|
||||||
|
self.editor
|
||||||
|
.data_form
|
||||||
|
.retain(|e| e.get_name().contains(name));
|
||||||
|
self.editor
|
||||||
|
.unterformular
|
||||||
|
.retain(|e| e.get_name().contains(name));
|
||||||
|
}
|
||||||
|
pub fn print_list_filtered(&mut self, name: &str) {
|
||||||
|
println!(
|
||||||
|
"Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte für '{}' sind gespeichert",
|
||||||
|
style(&self.info_xml.datum_xml).yellow(),
|
||||||
|
style(&self.info_xml.name).yellow(),
|
||||||
|
style(&self.info_xml.version).yellow(),
|
||||||
|
name
|
||||||
|
);
|
||||||
|
|
||||||
|
self.filter_by_name_contains(name);
|
||||||
|
|
||||||
|
Self::print_items("Merkmalskataloge", &self.editor.property_catalogue);
|
||||||
|
Self::print_items("Datenkataloge", &self.editor.data_catalogue);
|
||||||
|
Self::print_items("Formulare", &self.editor.data_form);
|
||||||
|
Self::print_items("Unterformulare", &self.editor.unterformular);
|
||||||
|
}
|
||||||
|
|
||||||
fn print_items(title: &str, list: &[impl Listable]) {
|
fn print_items(title: &str, list: &[impl Listable]) {
|
||||||
println!("\n{} {}", list.len(), style(title).underlined());
|
print!("\n{} {}", list.len(), style(title).underlined());
|
||||||
|
println!(
|
||||||
|
" - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert",
|
||||||
|
style("S").yellow()
|
||||||
|
);
|
||||||
list.iter()
|
list.iter()
|
||||||
.for_each(|entry| println!("{}", entry.to_listed_string()));
|
.for_each(|entry| println!("{}", entry.to_listed_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print_tree(&self) {
|
||||||
|
println!(
|
||||||
|
"Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte sind gespeichert",
|
||||||
|
style(&self.info_xml.datum_xml).yellow(),
|
||||||
|
style(&self.info_xml.name).yellow(),
|
||||||
|
style(&self.info_xml.version).yellow()
|
||||||
|
);
|
||||||
|
|
||||||
|
Self::print_items("Merkmalskataloge", &self.editor.property_catalogue);
|
||||||
|
self.print_items_tree("Datenkataloge", &self.editor.data_catalogue);
|
||||||
|
self.print_items_tree("Formulare", &self.editor.data_form);
|
||||||
|
self.print_items_tree("Unterformulare", &self.editor.unterformular);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_tree_filtered(&mut self, name: &str) {
|
||||||
|
println!(
|
||||||
|
"Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte für '{}' sind gespeichert",
|
||||||
|
style(&self.info_xml.datum_xml).yellow(),
|
||||||
|
style(&self.info_xml.name).yellow(),
|
||||||
|
style(&self.info_xml.version).yellow(),
|
||||||
|
name
|
||||||
|
);
|
||||||
|
|
||||||
|
self.filter_by_name_contains(name);
|
||||||
|
|
||||||
|
Self::print_items("Merkmalskataloge", &self.editor.property_catalogue);
|
||||||
|
self.print_items_tree("Datenkataloge", &self.editor.data_catalogue);
|
||||||
|
self.print_items_tree("Formulare", &self.editor.data_form);
|
||||||
|
self.print_items_tree("Unterformulare", &self.editor.unterformular);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_items_tree(&self, title: &str, list: &[impl Requires]) {
|
||||||
|
print!("\n{} {}", list.len(), style(title).underlined());
|
||||||
|
println!(
|
||||||
|
" - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert",
|
||||||
|
style("S").yellow()
|
||||||
|
);
|
||||||
|
list.iter()
|
||||||
|
.for_each(|entry| println!("{}", entry.to_requirement_string(self)));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sorted(&mut self) {
|
pub fn sorted(&mut self) {
|
||||||
self.editor
|
self.editor
|
||||||
.property_catalogue
|
.property_catalogue
|
||||||
@@ -91,24 +230,66 @@ impl OnkostarEditor {
|
|||||||
item.sorted();
|
item.sorted();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*self.editor
|
||||||
|
.data_form
|
||||||
|
.sort_unstable_by_key(|e| e.sorting_key());*/
|
||||||
|
|
||||||
self.editor
|
self.editor
|
||||||
.data_form
|
.data_form
|
||||||
.sort_unstable_by_key(|e| e.sorting_key());
|
.sort_unstable_by(DataForm::compare_by_requirement);
|
||||||
|
|
||||||
self.editor.data_form.iter_mut().for_each(|item| {
|
self.editor.data_form.iter_mut().for_each(|item| {
|
||||||
item.sorted();
|
item.sorted();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*self.editor
|
||||||
|
.unterformular
|
||||||
|
.sort_unstable_by_key(|e| e.sorting_key());*/
|
||||||
|
|
||||||
self.editor
|
self.editor
|
||||||
.unterformular
|
.unterformular
|
||||||
.sort_unstable_by_key(|e| e.sorting_key());
|
.sort_unstable_by(Unterformular::compare_by_requirement);
|
||||||
|
|
||||||
self.editor.unterformular.iter_mut().for_each(|item| {
|
self.editor.unterformular.iter_mut().for_each(|item| {
|
||||||
item.sorted();
|
item.sorted();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn strip_system_library_content(&mut self) {
|
||||||
|
self.editor
|
||||||
|
.property_catalogue
|
||||||
|
.retain(|e| !e.is_system_library_content());
|
||||||
|
|
||||||
|
self.editor
|
||||||
|
.data_catalogue
|
||||||
|
.retain(|e| !e.is_system_library_content());
|
||||||
|
|
||||||
|
self.editor
|
||||||
|
.data_form
|
||||||
|
.retain(|e| !e.is_system_library_content());
|
||||||
|
|
||||||
|
self.editor
|
||||||
|
.unterformular
|
||||||
|
.retain(|e| !e.is_system_library_content());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_diff(&mut self, other: &mut Self, strict: bool) {
|
pub fn print_diff(&mut self, other: &mut Self, strict: bool) {
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Datei A wurde am {} mit {} in Version {} erstellt.",
|
||||||
|
style(&self.info_xml.datum_xml).yellow(),
|
||||||
|
style(&self.info_xml.name).yellow(),
|
||||||
|
style(&self.info_xml.version).yellow()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Datei B wurde am {} mit {} in Version {} erstellt.",
|
||||||
|
style(&other.info_xml.datum_xml).yellow(),
|
||||||
|
style(&other.info_xml.name).yellow(),
|
||||||
|
style(&other.info_xml.version).yellow()
|
||||||
|
);
|
||||||
|
|
||||||
self.sorted();
|
self.sorted();
|
||||||
other.sorted();
|
other.sorted();
|
||||||
|
|
||||||
@@ -229,6 +410,89 @@ impl FromStr for OnkostarEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Checkable for OnkostarEditor {
|
||||||
|
fn check(&self) -> Vec<CheckNotice> {
|
||||||
|
// Inner form checks
|
||||||
|
|
||||||
|
let mut result = self
|
||||||
|
.editor
|
||||||
|
.data_form
|
||||||
|
.iter()
|
||||||
|
.flat_map(|entity| entity.check())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let other = &mut self
|
||||||
|
.editor
|
||||||
|
.unterformular
|
||||||
|
.iter()
|
||||||
|
.flat_map(|entity| entity.check())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
result.append(other);
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
|
||||||
|
let mut requirement_checked_forms = vec![];
|
||||||
|
|
||||||
|
fn requirement_error(
|
||||||
|
form: &impl Comparable,
|
||||||
|
item: &impl Comparable,
|
||||||
|
t: &str,
|
||||||
|
) -> CheckNotice {
|
||||||
|
CheckNotice::ErrorWithCode {
|
||||||
|
code: "2023-0004".to_string(),
|
||||||
|
description: format!(
|
||||||
|
"'{}' hat einen Verweis auf zuvor nicht definiertes {t} '{}' (OSTARSUPP-13212)",
|
||||||
|
form.get_name(),
|
||||||
|
item.get_name()
|
||||||
|
),
|
||||||
|
line: None,
|
||||||
|
example: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.editor.unterformular.iter().for_each(|form| {
|
||||||
|
requirement_checked_forms.push(form.get_name());
|
||||||
|
form.get_required_entries(self)
|
||||||
|
.iter()
|
||||||
|
.for_each(|entry| match entry {
|
||||||
|
Requirement::DataFormReference(item) => {
|
||||||
|
if !requirement_checked_forms.contains(&item.get_name()) {
|
||||||
|
result.push(requirement_error(form, *item, "Formular"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Requirement::UnterformularReference(item) => {
|
||||||
|
if !requirement_checked_forms.contains(&item.get_name()) {
|
||||||
|
result.push(requirement_error(form, *item, "Unterformular"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
self.editor.data_form.iter().for_each(|form| {
|
||||||
|
requirement_checked_forms.push(form.get_name());
|
||||||
|
form.get_required_entries(self)
|
||||||
|
.iter()
|
||||||
|
.for_each(|entry| match entry {
|
||||||
|
Requirement::DataFormReference(item) => {
|
||||||
|
if !requirement_checked_forms.contains(&item.get_name()) {
|
||||||
|
result.push(requirement_error(form, *item, "Formular"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Requirement::UnterformularReference(item) => {
|
||||||
|
if !requirement_checked_forms.contains(&item.get_name()) {
|
||||||
|
result.push(requirement_error(form, *item, "Unterformular"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct InfoXML {
|
pub struct InfoXML {
|
||||||
@@ -243,12 +507,12 @@ pub struct InfoXML {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
struct Editor {
|
struct Editor {
|
||||||
#[serde(rename = "PropertyCatalogue")]
|
#[serde(rename = "PropertyCatalogue", default)]
|
||||||
property_catalogue: Vec<PropertyCatalogue>,
|
property_catalogue: Vec<PropertyCatalogue>,
|
||||||
#[serde(rename = "DataCatalogue")]
|
#[serde(rename = "DataCatalogue", default)]
|
||||||
data_catalogue: Vec<DataCatalogue>,
|
data_catalogue: Vec<DataCatalogue>,
|
||||||
#[serde(rename = "Unterformular")]
|
#[serde(rename = "Unterformular", default)]
|
||||||
unterformular: Vec<Unterformular>,
|
unterformular: Vec<Unterformular>,
|
||||||
#[serde(rename = "DataForm")]
|
#[serde(rename = "DataForm", default)]
|
||||||
data_form: Vec<DataForm>,
|
data_form: Vec<DataForm>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
use console::style;
|
use console::style;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::model::{Comparable, Listable, Ordner, Sortable};
|
use crate::model::{Comparable, FolderContent, Listable, Ordner, Sortable};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
@@ -56,7 +56,11 @@ pub struct PropertyCatalogue {
|
|||||||
impl Listable for PropertyCatalogue {
|
impl Listable for PropertyCatalogue {
|
||||||
fn to_listed_string(&self) -> String {
|
fn to_listed_string(&self) -> String {
|
||||||
format!(
|
format!(
|
||||||
"Merkmalskatalog '{}' in Revision '{}'",
|
"Merkmalskatalog ({}) '{}' in Revision '{}'",
|
||||||
|
match self.is_system_library_content() {
|
||||||
|
true => style("S").yellow(),
|
||||||
|
_ => style("u"),
|
||||||
|
},
|
||||||
style(&self.name).yellow(),
|
style(&self.name).yellow(),
|
||||||
style(&self.revision).yellow()
|
style(&self.revision).yellow()
|
||||||
)
|
)
|
||||||
@@ -89,6 +93,12 @@ impl Comparable for PropertyCatalogue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FolderContent for PropertyCatalogue {
|
||||||
|
fn get_library_folder(&self) -> String {
|
||||||
|
self.ordner.bibliothek.name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Versions {
|
pub struct Versions {
|
||||||
@@ -117,12 +127,12 @@ pub struct Version {
|
|||||||
#[serde(rename = "Revision")]
|
#[serde(rename = "Revision")]
|
||||||
revision: u16,
|
revision: u16,
|
||||||
#[serde(rename = "Entries")]
|
#[serde(rename = "Entries")]
|
||||||
entries: VersionEntries,
|
entries: Option<VersionEntries>,
|
||||||
#[serde(rename = "Abbildung")]
|
#[serde(rename = "Abbildung")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
abbildung: Option<Vec<Abbildung>>,
|
abbildung: Option<Vec<Abbildung>>,
|
||||||
#[serde(rename = "Categories")]
|
#[serde(rename = "Categories")]
|
||||||
categories: Categories,
|
categories: Option<Categories>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sortable for Version {
|
impl Sortable for Version {
|
||||||
@@ -141,19 +151,23 @@ impl Sortable for Version {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.entries
|
if let Some(ref mut entries) = self.entries {
|
||||||
|
entries
|
||||||
.content
|
.content
|
||||||
.sort_unstable_by_key(|item| item.sorting_key());
|
.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
self.entries.content.iter_mut().for_each(|item| {
|
entries.content.iter_mut().for_each(|item| {
|
||||||
item.sorted();
|
item.sorted();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.categories
|
if let Some(ref mut categories) = self.categories {
|
||||||
|
categories
|
||||||
.content
|
.content
|
||||||
.sort_unstable_by_key(|item| item.sorting_key());
|
.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
self.categories.content.iter_mut().for_each(|item| {
|
categories.content.iter_mut().for_each(|item| {
|
||||||
item.sorted();
|
item.sorted();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -174,13 +188,16 @@ pub struct VersionEntry {
|
|||||||
#[serde(rename = "ShortDescription")]
|
#[serde(rename = "ShortDescription")]
|
||||||
short_description: String,
|
short_description: String,
|
||||||
#[serde(rename = "Description")]
|
#[serde(rename = "Description")]
|
||||||
description: String,
|
description: Option<String>,
|
||||||
#[serde(rename = "Synonyms", default)]
|
#[serde(rename = "Synonyms", default)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
synonyms: Option<String>,
|
synonyms: Option<String>,
|
||||||
#[serde(rename = "Note", default)]
|
#[serde(rename = "Note", default)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
note: Option<String>,
|
note: Option<String>,
|
||||||
|
#[serde(rename = "Type", default)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
type_: Option<String>,
|
||||||
#[serde(rename = "Position")]
|
#[serde(rename = "Position")]
|
||||||
position: String,
|
position: String,
|
||||||
}
|
}
|
||||||
@@ -321,8 +338,9 @@ pub struct AbbildungEntry {
|
|||||||
#[serde(rename = "Synonyms")]
|
#[serde(rename = "Synonyms")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
synonyms: Option<String>,
|
synonyms: Option<String>,
|
||||||
#[serde(rename = "Note")]
|
#[serde(rename = "Note", default)]
|
||||||
note: String,
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
note: Option<String>,
|
||||||
#[serde(rename = "Position")]
|
#[serde(rename = "Position")]
|
||||||
position: String,
|
position: String,
|
||||||
}
|
}
|
||||||
|
|||||||
165
src/model/requirements.rs
Normal file
165
src/model/requirements.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::model::data_catalogue::DataCatalogue;
|
||||||
|
use crate::model::data_form::DataForm;
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::model::property_catalogue::PropertyCatalogue;
|
||||||
|
use crate::model::unterformular::Unterformular;
|
||||||
|
use crate::model::{Comparable, Listable, Sortable};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum Requirement<'a> {
|
||||||
|
PropertyCatalogue(&'a PropertyCatalogue),
|
||||||
|
DataCatalogue(&'a DataCatalogue),
|
||||||
|
ExternalPropertyCatalogue(String),
|
||||||
|
ExternalDataCatalogue(String),
|
||||||
|
DataFormReference(&'a DataForm),
|
||||||
|
UnterformularReference(&'a Unterformular),
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ExternalDataFormReference(String),
|
||||||
|
ExternalUnterformularReference(String),
|
||||||
|
|
||||||
|
DataFormSubform(&'a DataForm),
|
||||||
|
UnterformularSubform(&'a Unterformular),
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ExternalDataFormSubform(String),
|
||||||
|
ExternalUnterformularSubform(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sortable for Requirement<'_> {
|
||||||
|
fn sorting_key(&self) -> String {
|
||||||
|
match self {
|
||||||
|
Requirement::PropertyCatalogue(item) => item.get_name(),
|
||||||
|
Requirement::DataCatalogue(item) => item.get_name(),
|
||||||
|
Requirement::DataFormReference(item) => item.get_name(),
|
||||||
|
Requirement::UnterformularReference(item) => item.get_name(),
|
||||||
|
Requirement::DataFormSubform(item) => item.get_name(),
|
||||||
|
Requirement::UnterformularSubform(item) => item.get_name(),
|
||||||
|
Requirement::ExternalPropertyCatalogue(name)
|
||||||
|
| Requirement::ExternalDataCatalogue(name)
|
||||||
|
| Requirement::ExternalDataFormReference(name)
|
||||||
|
| Requirement::ExternalDataFormSubform(name)
|
||||||
|
| Requirement::ExternalUnterformularReference(name)
|
||||||
|
| Requirement::ExternalUnterformularSubform(name) => name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Requirement<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let str = match self {
|
||||||
|
Requirement::PropertyCatalogue(item) => item.to_listed_string(),
|
||||||
|
Requirement::DataCatalogue(item) => item.to_listed_string(),
|
||||||
|
Requirement::DataFormReference(item) => item.to_listed_string(),
|
||||||
|
Requirement::UnterformularReference(item) => item.to_listed_string(),
|
||||||
|
Requirement::DataFormSubform(item) => item.to_listed_string(),
|
||||||
|
Requirement::UnterformularSubform(item) => item.to_listed_string(),
|
||||||
|
Requirement::ExternalPropertyCatalogue(name) => {
|
||||||
|
format!("Merkmalskatalog (-) '{}' - hier nicht enthalten", name)
|
||||||
|
}
|
||||||
|
Requirement::ExternalDataCatalogue(name) => {
|
||||||
|
format!("Datenkatalog (-) '{}' - hier nicht enthalten", name)
|
||||||
|
}
|
||||||
|
Requirement::ExternalDataFormReference(name)
|
||||||
|
| Requirement::ExternalDataFormSubform(name) => {
|
||||||
|
format!("Formular (-) '{}' - hier nicht enthalten", name)
|
||||||
|
}
|
||||||
|
Requirement::ExternalUnterformularReference(name)
|
||||||
|
| Requirement::ExternalUnterformularSubform(name) => {
|
||||||
|
format!("Unterformular (-) '{}' - hier nicht enthalten", name)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
write!(f, "{}", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Requires
|
||||||
|
where
|
||||||
|
Self: Listable,
|
||||||
|
{
|
||||||
|
fn requires_form_reference(&self, _: &str) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_subform(&self, _: &str) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement>;
|
||||||
|
|
||||||
|
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String {
|
||||||
|
format!(
|
||||||
|
"{}\n{}",
|
||||||
|
self.to_listed_string(),
|
||||||
|
self.get_required_entries(all)
|
||||||
|
.iter()
|
||||||
|
.map(|entry| match entry {
|
||||||
|
Requirement::DataCatalogue(x) => {
|
||||||
|
let inner = x
|
||||||
|
.get_required_entries(all)
|
||||||
|
.iter()
|
||||||
|
.map(|inner_entry| match inner_entry {
|
||||||
|
Requirement::PropertyCatalogue(_) => Some(inner_entry.to_string()),
|
||||||
|
Requirement::ExternalPropertyCatalogue(_) => {
|
||||||
|
Some(inner_entry.to_string())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter(Option::is_some)
|
||||||
|
.map(|item| format!(" - {}\n", item.unwrap()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
if inner.is_empty() {
|
||||||
|
Some(format!(" + {}\n", x.to_listed_string()))
|
||||||
|
} else {
|
||||||
|
Some(format!(" + {}\n{}", x.to_listed_string(), inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Requirement::ExternalDataCatalogue(_) => {
|
||||||
|
Some(format!(" + {}\n", entry))
|
||||||
|
}
|
||||||
|
Requirement::DataFormReference(_)
|
||||||
|
| Requirement::ExternalDataFormReference(_)
|
||||||
|
| Requirement::UnterformularReference(_)
|
||||||
|
| Requirement::ExternalUnterformularReference(_) => {
|
||||||
|
Some(format!(" > {}\n", entry))
|
||||||
|
}
|
||||||
|
Requirement::DataFormSubform(_)
|
||||||
|
| Requirement::ExternalDataFormSubform(_)
|
||||||
|
| Requirement::UnterformularSubform(_)
|
||||||
|
| Requirement::ExternalUnterformularSubform(_) => {
|
||||||
|
Some(format!(" * {}\n", entry))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter(Option::is_some)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,20 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use crate::checks::CheckNotice::ErrorWithCode;
|
||||||
|
use crate::checks::{CheckNotice, Checkable};
|
||||||
use console::style;
|
use console::style;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::model::onkostar_editor::OnkostarEditor;
|
||||||
|
use crate::model::requirements::{Requirement, Requires};
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
|
apply_profile_to_form_entry, apply_profile_to_form_field, Ansichten, Comparable, Entries,
|
||||||
FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
|
Filter, FolderContent, FormEntry, FormEntryContainer, Kennzahlen, Listable, MenuCategory,
|
||||||
|
PlausibilityRules, PunkteKategorien, RefEntries, Script, Sortable,
|
||||||
};
|
};
|
||||||
use crate::model::{Haeufigkeiten, Ordner};
|
use crate::model::{Haeufigkeiten, Ordner};
|
||||||
use crate::profile::Profile;
|
use crate::profile::Profile;
|
||||||
@@ -74,7 +82,7 @@ pub struct Unterformular {
|
|||||||
#[serde(rename = "EmailTemplate")]
|
#[serde(rename = "EmailTemplate")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
mail_template: Option<String>,
|
mail_template: Option<String>,
|
||||||
#[serde(rename = "ErkrankungText")]
|
#[serde(rename = "ErkrankungText", default)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
erkrankung_text: Option<String>,
|
erkrankung_text: Option<String>,
|
||||||
#[serde(rename = "ErkrankungTextLong")]
|
#[serde(rename = "ErkrankungTextLong")]
|
||||||
@@ -91,6 +99,9 @@ pub struct Unterformular {
|
|||||||
erkrankung_big_summary: Option<String>,
|
erkrankung_big_summary: Option<String>,
|
||||||
#[serde(rename = "Kontext")]
|
#[serde(rename = "Kontext")]
|
||||||
kontext: String,
|
kontext: String,
|
||||||
|
#[serde(rename = "Datenart")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
datenart: Option<String>,
|
||||||
#[serde(rename = "TudokReadonly")]
|
#[serde(rename = "TudokReadonly")]
|
||||||
tudok_readonly: bool,
|
tudok_readonly: bool,
|
||||||
#[serde(rename = "VitalstatusRelevant")]
|
#[serde(rename = "VitalstatusRelevant")]
|
||||||
@@ -153,12 +164,15 @@ pub struct Unterformular {
|
|||||||
#[serde(rename = "Haeufigkeiten")]
|
#[serde(rename = "Haeufigkeiten")]
|
||||||
haeufigkeiten: Haeufigkeiten,
|
haeufigkeiten: Haeufigkeiten,
|
||||||
#[serde(rename = "Kennzahlen")]
|
#[serde(rename = "Kennzahlen")]
|
||||||
kennzahlen: String,
|
kennzahlen: Kennzahlen,
|
||||||
#[serde(rename = "Ordner")]
|
#[serde(rename = "Ordner")]
|
||||||
ordner: Ordner,
|
ordner: Ordner,
|
||||||
#[serde(rename = "MenuCategory")]
|
#[serde(rename = "MenuCategory")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
menu_category: Option<MenuCategory>,
|
menu_category: Option<MenuCategory>,
|
||||||
|
#[serde(rename = "PunkteKategorien")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
punkte_kategorien: Option<PunkteKategorien>,
|
||||||
#[serde(rename = "Ansichten")]
|
#[serde(rename = "Ansichten")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
ansichten: Option<Ansichten>,
|
ansichten: Option<Ansichten>,
|
||||||
@@ -176,6 +190,12 @@ impl FormEntryContainer for Unterformular {
|
|||||||
apply_profile_to_form_entry(entry, form_reference)
|
apply_profile_to_form_entry(entry, form_reference)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Hide form field using filter set to "false" if requested
|
||||||
|
profile_form
|
||||||
|
.form_fields
|
||||||
|
.iter()
|
||||||
|
.for_each(|form_field| apply_profile_to_form_field(entry, form_field));
|
||||||
|
|
||||||
if let Some(menu_category) = &profile_form.menu_category {
|
if let Some(menu_category) = &profile_form.menu_category {
|
||||||
self.menu_category = Some(MenuCategory {
|
self.menu_category = Some(MenuCategory {
|
||||||
name: menu_category.name.clone(),
|
name: menu_category.name.clone(),
|
||||||
@@ -191,18 +211,14 @@ impl FormEntryContainer for Unterformular {
|
|||||||
|
|
||||||
impl Listable for Unterformular {
|
impl Listable for Unterformular {
|
||||||
fn to_listed_string(&self) -> String {
|
fn to_listed_string(&self) -> String {
|
||||||
if self.hat_unterformulare {
|
format!(
|
||||||
return format!(
|
"Unterformular ({}) '{}' in Revision '{}'",
|
||||||
"Unterformular '{}' in Revision '{}' {}",
|
match self.is_system_library_content() {
|
||||||
|
true => style("S").yellow(),
|
||||||
|
_ => style("u"),
|
||||||
|
},
|
||||||
style(&self.name).yellow(),
|
style(&self.name).yellow(),
|
||||||
style(&self.revision).yellow(),
|
style(&self.revision).yellow(),
|
||||||
style("Unterformular mit Markierung 'hat Unterformulare'!").red()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
format!(
|
|
||||||
"Unterformular '{}' in Revision '{}'",
|
|
||||||
style(&self.name).yellow(),
|
|
||||||
style(&self.revision).yellow()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,6 +260,139 @@ impl Comparable for Unterformular {
|
|||||||
fn get_revision(&self) -> u16 {
|
fn get_revision(&self) -> u16 {
|
||||||
self.revision
|
self.revision
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compare_by_requirement(a: &Self, b: &Self) -> Ordering {
|
||||||
|
if a.get_name() == b.get_name()
|
||||||
|
|| a.is_system_library_content()
|
||||||
|
|| b.is_system_library_content()
|
||||||
|
{
|
||||||
|
return Ordering::Equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.requires_form_reference(&b.get_name()) || a.requires_subform(&b.get_name()) {
|
||||||
|
return Ordering::Greater;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ordering::Less
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Requires for Unterformular {
|
||||||
|
fn requires_form_reference(&self, name: &str) -> bool {
|
||||||
|
self.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
item.type_ == "formReference"
|
||||||
|
&& match item.referenced_data_form.as_ref() {
|
||||||
|
Some(refname) => refname == name,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|&it| it)
|
||||||
|
.last()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
fn requires_subform(&self, name: &str) -> bool {
|
||||||
|
self.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.map(|item| {
|
||||||
|
item.type_ == "subform"
|
||||||
|
&& match item.referenced_data_form.as_ref() {
|
||||||
|
Some(refname) => refname == name,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|&it| it)
|
||||||
|
.last()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
|
||||||
|
let mut result = self
|
||||||
|
.data_catalogues
|
||||||
|
.data_catalogue
|
||||||
|
.iter()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_catalogue(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataCatalogue(contained),
|
||||||
|
None => Requirement::ExternalDataCatalogue(entry.to_string()),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
result.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
|
||||||
|
let referenced_forms = &mut self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|&entry| entry.get_type() == "formReference")
|
||||||
|
.filter_map(|entry| match &entry.referenced_data_form {
|
||||||
|
Some(name) => Some(name),
|
||||||
|
None => None,
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_form(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataFormReference(contained),
|
||||||
|
None => match all.find_unterformular(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::UnterformularReference(contained),
|
||||||
|
None => Requirement::ExternalUnterformularReference(entry.to_string()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
referenced_forms.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
result.append(referenced_forms);
|
||||||
|
|
||||||
|
let sub_forms = &mut self
|
||||||
|
.entries
|
||||||
|
.entry
|
||||||
|
.iter()
|
||||||
|
.filter(|&entry| entry.get_type() == "subform")
|
||||||
|
.filter_map(|entry| match &entry.referenced_data_form {
|
||||||
|
Some(name) => Some(name),
|
||||||
|
None => None,
|
||||||
|
})
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.map(|entry| match all.find_data_form(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::DataFormSubform(contained),
|
||||||
|
None => match all.find_unterformular(entry.as_str()) {
|
||||||
|
Some(contained) => Requirement::UnterformularSubform(contained),
|
||||||
|
None => Requirement::ExternalUnterformularSubform(entry.to_string()),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
sub_forms.sort_unstable_by_key(|item| item.sorting_key());
|
||||||
|
result.append(sub_forms);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FolderContent for Unterformular {
|
||||||
|
fn get_library_folder(&self) -> String {
|
||||||
|
self.ordner.bibliothek.name.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Checkable for Unterformular {
|
||||||
|
fn check(&self) -> Vec<CheckNotice> {
|
||||||
|
if self.hat_unterformulare {
|
||||||
|
return vec![ErrorWithCode {
|
||||||
|
code: "2023-0001".to_string(),
|
||||||
|
description: format!(
|
||||||
|
"Unterformular '{}' mit Markierung 'hat Unterformulare'",
|
||||||
|
self.name
|
||||||
|
),
|
||||||
|
line: None,
|
||||||
|
example: None,
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@@ -314,6 +463,9 @@ pub struct Entry {
|
|||||||
grafik_ausrichtung: Option<String>,
|
grafik_ausrichtung: Option<String>,
|
||||||
#[serde(rename = "Mandatory")]
|
#[serde(rename = "Mandatory")]
|
||||||
mandatory: String,
|
mandatory: String,
|
||||||
|
#[serde(rename = "Datenart", default)]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
datenart: Option<String>,
|
||||||
#[serde(rename = "Filter")]
|
#[serde(rename = "Filter")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
filter: Option<Filter>,
|
filter: Option<Filter>,
|
||||||
@@ -333,6 +485,9 @@ pub struct Entry {
|
|||||||
#[serde(rename = "AnzeigeAuswahl")]
|
#[serde(rename = "AnzeigeAuswahl")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
anzeige_auswahl: Option<String>,
|
anzeige_auswahl: Option<String>,
|
||||||
|
#[serde(rename = "Druckvorlage")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
druckvorlage: Option<String>,
|
||||||
#[serde(rename = "VersionFrom")]
|
#[serde(rename = "VersionFrom")]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
version_from: Option<String>,
|
version_from: Option<String>,
|
||||||
@@ -340,6 +495,9 @@ pub struct Entry {
|
|||||||
speichern: String,
|
speichern: String,
|
||||||
#[serde(rename = "LeerAusblenden")]
|
#[serde(rename = "LeerAusblenden")]
|
||||||
leer_ausblenden: bool,
|
leer_ausblenden: bool,
|
||||||
|
#[serde(rename = "Inhalt")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
inhalt: Option<String>,
|
||||||
#[serde(rename = "GeschlossenAnzeigen")]
|
#[serde(rename = "GeschlossenAnzeigen")]
|
||||||
geschlossen_anzeigen: bool,
|
geschlossen_anzeigen: bool,
|
||||||
#[serde(rename = "Min")]
|
#[serde(rename = "Min")]
|
||||||
@@ -422,12 +580,21 @@ pub struct Entry {
|
|||||||
in_auswertung: bool,
|
in_auswertung: bool,
|
||||||
#[serde(rename = "InAuswertungGraph")]
|
#[serde(rename = "InAuswertungGraph")]
|
||||||
in_auswertung_graph: bool,
|
in_auswertung_graph: bool,
|
||||||
|
#[serde(rename = "FragebogenItemNummer")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
fragebogen_item_nummer: Option<u8>,
|
||||||
|
#[serde(rename = "Score")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
score: Option<String>,
|
||||||
#[serde(rename = "AlignmentPatModul")]
|
#[serde(rename = "AlignmentPatModul")]
|
||||||
alignment_pat_modul: String,
|
alignment_pat_modul: String,
|
||||||
#[serde(rename = "DirectionPatModul")]
|
#[serde(rename = "DirectionPatModul")]
|
||||||
direction_pat_modul: String,
|
direction_pat_modul: String,
|
||||||
#[serde(rename = "SeitenumbruchPatModul")]
|
#[serde(rename = "SeitenumbruchPatModul")]
|
||||||
seitenumbruch_pat_modul: bool,
|
seitenumbruch_pat_modul: bool,
|
||||||
|
#[serde(rename = "Kontaktliste")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
kontaktliste: Option<String>,
|
||||||
#[serde(rename = "MarkierungIgnorieren")]
|
#[serde(rename = "MarkierungIgnorieren")]
|
||||||
markierung_ignorieren: bool,
|
markierung_ignorieren: bool,
|
||||||
#[serde(rename = "SucheArt")]
|
#[serde(rename = "SucheArt")]
|
||||||
@@ -474,6 +641,15 @@ impl FormEntry for Entry {
|
|||||||
valid: true,
|
valid: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hide(&mut self) {
|
||||||
|
self.filter = Some(Filter {
|
||||||
|
condition: "false".into(),
|
||||||
|
valid: true,
|
||||||
|
ref_entries: Some(RefEntries { ref_entry: None }),
|
||||||
|
});
|
||||||
|
self.speichern = "0".into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sortable for Entry {
|
impl Sortable for Entry {
|
||||||
|
|||||||
@@ -34,6 +34,19 @@ pub struct Profile {
|
|||||||
pub forms: Vec<Form>,
|
pub forms: Vec<Form>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Profile {
|
||||||
|
pub fn embedded_profile(name: &str) -> Result<Profile, String> {
|
||||||
|
let s = match name {
|
||||||
|
"UKM" => include_str!("../examples/dnpm-ukm.yml"),
|
||||||
|
"UKW" => include_str!("../examples/dnpm-ukw.yml"),
|
||||||
|
"UMG" => include_str!("../examples/dnpm-umg.yml"),
|
||||||
|
_ => return Err(format!("Not an embedded profile: '{name}'")),
|
||||||
|
};
|
||||||
|
|
||||||
|
Profile::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromStr for Profile {
|
impl FromStr for Profile {
|
||||||
type Err = String;
|
type Err = String;
|
||||||
|
|
||||||
@@ -50,6 +63,8 @@ pub struct Form {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub form_references: Vec<FormReference>,
|
pub form_references: Vec<FormReference>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub form_fields: Vec<FormField>,
|
||||||
pub menu_category: Option<MenuCategory>,
|
pub menu_category: Option<MenuCategory>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +83,13 @@ impl FormReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct FormField {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub hide: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct MenuCategory {
|
pub struct MenuCategory {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -93,6 +115,9 @@ mod tests {
|
|||||||
referenced_data_form: 'OS.Tumorkonferenz.VarianteUKW'
|
referenced_data_form: 'OS.Tumorkonferenz.VarianteUKW'
|
||||||
anzeige: 'Datum: {Datum}'
|
anzeige: 'Datum: {Datum}'
|
||||||
anzeige_auswahl: 'TK vom {Datum}'
|
anzeige_auswahl: 'TK vom {Datum}'
|
||||||
|
form_fields:
|
||||||
|
- name: eingabefeld
|
||||||
|
hide: true
|
||||||
";
|
";
|
||||||
|
|
||||||
match Profile::from_str(content) {
|
match Profile::from_str(content) {
|
||||||
@@ -101,6 +126,7 @@ mod tests {
|
|||||||
assert_eq!(profile.forms[0].name, "DNPM Therapieplan");
|
assert_eq!(profile.forms[0].name, "DNPM Therapieplan");
|
||||||
assert!(profile.forms[0].menu_category.is_some());
|
assert!(profile.forms[0].menu_category.is_some());
|
||||||
assert_eq!(profile.forms[0].form_references.len(), 1);
|
assert_eq!(profile.forms[0].form_references.len(), 1);
|
||||||
|
assert_eq!(profile.forms[0].form_fields.len(), 1)
|
||||||
}
|
}
|
||||||
Err(e) => panic!("Cannot deserialize profile: {}", e),
|
Err(e) => panic!("Cannot deserialize profile: {}", e),
|
||||||
}
|
}
|
||||||
@@ -212,4 +238,29 @@ mod tests {
|
|||||||
let actual = Profile::from_str(content);
|
let actual = Profile::from_str(content);
|
||||||
assert!(actual.is_err());
|
assert!(actual.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_deserialize_form_fields() {
|
||||||
|
let content = "forms:
|
||||||
|
- name: 'DNPM Therapieplan'
|
||||||
|
form_fields:
|
||||||
|
- name: formularfeld_to_keep
|
||||||
|
hide: false
|
||||||
|
- name: formularfeld_to_hide
|
||||||
|
hide: true
|
||||||
|
";
|
||||||
|
|
||||||
|
match Profile::from_str(content) {
|
||||||
|
Ok(profile) => {
|
||||||
|
assert_eq!(profile.forms.len(), 1);
|
||||||
|
assert_eq!(profile.forms[0].name, "DNPM Therapieplan");
|
||||||
|
assert_eq!(profile.forms[0].form_fields.len(), 2);
|
||||||
|
assert_eq!(profile.forms[0].form_fields[0].name, "formularfeld_to_keep");
|
||||||
|
assert!(!profile.forms[0].form_fields[0].hide);
|
||||||
|
assert_eq!(profile.forms[0].form_fields[1].name, "formularfeld_to_hide");
|
||||||
|
assert!(profile.forms[0].form_fields[1].hide);
|
||||||
|
}
|
||||||
|
Err(e) => panic!("Cannot deserialize profile: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
135
src/unzip_osb.rs
Normal file
135
src/unzip_osb.rs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use console::style;
|
||||||
|
use deob::deobfuscate;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
|
macro_rules! started {
|
||||||
|
( $o:expr ) => {
|
||||||
|
println!("{: <6}{}", style("[..]").cyan(), $o);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ok {
|
||||||
|
( $o:expr ) => {
|
||||||
|
use console::Term;
|
||||||
|
let _ = Term::stdout().move_cursor_up(1);
|
||||||
|
println!("{: <6}{}", style("[OK]").green(), $o);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! error {
|
||||||
|
( $o:expr, $e:expr ) => {
|
||||||
|
use console::Term;
|
||||||
|
let _ = Term::stdout().move_cursor_up(1);
|
||||||
|
println!("{: <6}{} - Error: {}", style("[ERR]").red(), $o, $e);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unzip_osb_using_password(path: &str, dir: &str, password: &str) {
|
||||||
|
println!("Entpacke OSB-Datei {}\n", style(path).yellow());
|
||||||
|
|
||||||
|
let file = match fs::File::open(path) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"{: <6}Abbruch! - Kann Datei nicht entpacken: {}",
|
||||||
|
style("[ERR]").red(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut archive = match zip::ZipArchive::new(file) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"{: <6}Abbruch! - Kann Datei nicht entpacken: {}",
|
||||||
|
style("[ERR]").red(),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..archive.len() {
|
||||||
|
let mut file = if let Ok(Ok(file)) = archive.by_index_decrypt(i, password.as_bytes()) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"{: <6}Abbruch! - Kann Datei nicht entpacken",
|
||||||
|
style("[ERR]").red()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let outpath = match file.enclosed_name() {
|
||||||
|
Some(path) => Path::new(dir).join(path.to_owned()),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
started!(outpath.display());
|
||||||
|
|
||||||
|
if !file.is_dir() {
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
|
if !p.exists() {
|
||||||
|
fs::create_dir_all(p).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut outfile = match fs::File::create(&outpath) {
|
||||||
|
Ok(file) => file,
|
||||||
|
Err(err) => {
|
||||||
|
error!(outpath.display(), err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match io::copy(&mut file, &mut outfile) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
error!(outpath.display(), err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok!(outpath.display());
|
||||||
|
} else {
|
||||||
|
if !outpath.exists() {
|
||||||
|
match fs::create_dir_all(&outpath) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => {
|
||||||
|
error!(outpath.display(), err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok!(outpath.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unzip_osb(path: &str, dir: &str) {
|
||||||
|
unzip_osb_using_password(path, dir, deobfuscate(env!("OSB_KEY").trim()).as_str());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user