12 Commits

13 changed files with 553 additions and 68 deletions

101
Cargo.lock generated
View File

@@ -4,27 +4,15 @@ version = 3
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.0" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.3.4" version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80672091db20273a15cf9fdd4e47ed43b5091ec9841bf4c6145c9dfbbcae09ed" checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -33,20 +21,19 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.3.4" version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636" checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"bitflags",
"clap_lex", "clap_lex",
] ]
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.3.2" version = "4.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@@ -80,10 +67,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]] [[package]]
name = "hashbrown" name = "equivalent"
version = "0.12.3" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]] [[package]]
name = "heck" name = "heck"
@@ -93,19 +86,19 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [ dependencies = [
"autocfg", "equivalent",
"hashbrown", "hashbrown",
] ]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.6" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
@@ -115,9 +108,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.146" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]] [[package]]
name = "memchr" name = "memchr"
@@ -133,7 +126,7 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]] [[package]]
name = "osc-variant" name = "osc-variant"
version = "0.1.0" version = "0.2.0"
dependencies = [ dependencies = [
"clap", "clap",
"console", "console",
@@ -145,18 +138,18 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.60" version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.28.2" version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
@@ -164,33 +157,33 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.28" version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.13" version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.164" version = "1.0.182"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" checksum = "bdb30a74471f5b7a1fa299f40b4bf1be93af61116df95465b2b5fc419331e430"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.164" version = "1.0.182"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" checksum = "6f4c2c6ea4bc09b5c419012eafcdb0fcef1d9119d626c8f3a0708a5b92d38a70"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -199,9 +192,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_yaml" name = "serde_yaml"
version = "0.9.21" version = "0.9.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"itoa", "itoa",
@@ -212,9 +205,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.18" version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -223,9 +216,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.9" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
@@ -235,9 +228,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
version = "0.2.8" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
@@ -307,6 +300,6 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
version = "0.8.14" version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"

View File

@@ -1,13 +1,17 @@
[package] [package]
name = "osc-variant" name = "osc-variant"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"]
description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort"
license = "MIT"
readme = "README.md"
[dependencies] [dependencies]
clap = { version = "4.3", features = ["std", "help", "usage", "derive", "error-context" ], default-features = false } clap = { version = "4.3", 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.28", features = ["escape-html", "serialize"], default-features=false } quick-xml = { version = "0.30", features = ["escape-html", "serialize"], default-features = false }
xml-rs = "0.8" xml-rs = "0.8"
console = "0.15" console = "0.15"
@@ -17,3 +21,6 @@ codegen-units = 1
lto = "thin" lto = "thin"
strip = true strip = true
panic = "abort" panic = "abort"
[package.metadata.deb]
copyright = "Copyright (c) 2023 Comprehensive Cancer Center Mainfranken"

View File

@@ -38,7 +38,11 @@ win-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
.PHONE: clean .PHONY: install
install:
cargo install --path .
.PHONY: clean
clean: clean:
cargo clean cargo clean
rm -rf osc-variant 2>/dev/null || true rm -rf osc-variant 2>/dev/null || true

View File

@@ -22,6 +22,20 @@ Zum Auflisten der Inhalte einer Datei wird folgender Befehl verwendet:
osc-variant list meine-beispieldatei.osc osc-variant list meine-beispieldatei.osc
``` ```
Zum Vergleich zweier OSC-Dateien wird der Unterbefehl `diff` verwendet.
Der optionale Parameter `--strict` vergleicht auch den Inhalt der OSC-Datei.
Ohne diesen wird nur das Vorhandensein von Inhalten und die Revision verglichen.
```
osc-variant diff meine-beispieldatei.osc andere-beispieldatei.osc
```
bzw.
```
osc-variant diff meine-beispieldatei.osc andere-beispieldatei.osc --strict
```
Zum Anpassen des Inhalts einer Datei: Zum Anpassen des Inhalts einer Datei:
``` ```
@@ -44,6 +58,13 @@ Hierzu ist die Option `--compact` vorgesehen. Es können, je nach Datei, bis zu
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 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.
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.
## Profile ## Profile
Zum Erstellen von Varianten einer OSC-Datei wird eine Profildatei im YAML-Format verwendet. Zum Erstellen von Varianten einer OSC-Datei wird eine Profildatei im YAML-Format verwendet.

View File

@@ -4,3 +4,24 @@ 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}'
- name: 'DNPM Therapieplan'
form_references:
- name: referstemtb
referenced_data_form: 'MR.MTB_Empfehlung'
anzeige_auswahl: 'MTB vom {Datum}'
- name: reftkhumangenber
referenced_data_form: 'MR.MTB_Empfehlung'
anzeige_auswahl: 'MTB vom {Datum}'
- name: reftkreevaluation
referenced_data_form: 'MR.MTB_Empfehlung'
anzeige_auswahl: 'MTB vom {Datum}'
- name: 'DNPM UF Einzelempfehlung'
form_references:
- name: mtb
referenced_data_form: 'MR.MTB_Empfehlung'
anzeige_auswahl: 'MTB vom {Datum}'
- name: 'DNPM UF Rebiopsie'
form_references:
- name: reftumorkonferenz
referenced_data_form: 'MR.MTB_Empfehlung'
anzeige_auswahl: 'MTB vom {Datum}'

View File

@@ -52,5 +52,17 @@ pub enum Command {
outputfile: Option<String>, outputfile: Option<String>,
#[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(
long = "x-sorted",
help = "EXPERIMENTELL: Sortiere Kataloge und Formulare nach Name (Optional). Kann negative Auswirkungen auf den ordnungsgemäßen Import haben."
)]
sorted: bool,
},
#[command(about = "Vergleiche zwei Dateien anhand der Revision der enthaltenen Inhalte")]
Diff {
inputfile_a: String,
inputfile_b: String,
#[arg(long = "strict", help = "Strikter Vergleich des Inhalts")]
strict: bool,
}, },
} }

View File

@@ -31,6 +31,7 @@ use std::ops::Add;
use std::str::FromStr; use std::str::FromStr;
use clap::Parser; use clap::Parser;
use console::style;
use quick_xml::se::Serializer; use quick_xml::se::Serializer;
use serde::Serialize; use serde::Serialize;
@@ -121,6 +122,7 @@ fn main() -> Result<(), Box<dyn Error>> {
profile, profile,
outputfile, outputfile,
compact, compact,
sorted,
} => { } => {
let data = &mut read_inputfile(inputfile)?; let data = &mut read_inputfile(inputfile)?;
@@ -131,6 +133,10 @@ fn main() -> Result<(), Box<dyn Error>> {
data.apply_profile(&profile); data.apply_profile(&profile);
} }
if sorted {
data.sorted();
}
let mut buf = String::new(); let mut buf = String::new();
let mut serializer = Serializer::new(&mut buf); let mut serializer = Serializer::new(&mut buf);
@@ -157,6 +163,22 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
} }
} }
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(())

View File

@@ -25,7 +25,7 @@
use console::style; use console::style;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::model::{Listable, Ordner, Sortable}; use crate::model::{Comparable, Listable, Ordner, Sortable};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
@@ -70,6 +70,26 @@ impl Sortable for DataCatalogue {
fn sorting_key(&self) -> String { fn sorting_key(&self) -> String {
self.name.clone() self.name.clone()
} }
fn sorted(&mut self) -> &Self {
self.entries
.entry
.sort_unstable_by_key(|item| item.sorting_key());
self.entries.entry.iter_mut().for_each(|item| {
item.sorted();
});
self
}
}
impl Comparable for DataCatalogue {
fn get_name(&self) -> String {
self.name.clone()
}
fn get_revision(&self) -> u16 {
self.revision
}
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -141,6 +161,23 @@ pub struct Entry {
revision: u16, revision: u16,
} }
impl Sortable for Entry {
fn sorting_key(&self) -> String {
self.name.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
if let Some(ref mut use_) = self.use_ {
use_.program_module
.sort_unstable_by_key(|item| item.sorting_key())
}
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Use { pub struct Use {
@@ -156,3 +193,9 @@ pub struct ProgramModule {
#[serde(rename = "@name")] #[serde(rename = "@name")]
name: String, name: String,
} }
impl Sortable for ProgramModule {
fn sorting_key(&self) -> String {
format!("{}-{}", self.program, self.name)
}
}

View File

@@ -26,8 +26,8 @@ use console::style;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::model::{ use crate::model::{
apply_profile_to_form_entry, Ansichten, Entries, Filter, FormEntry, FormEntryContainer, apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
Listable, MenuCategory, PlausibilityRules, Script, Sortable, FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
}; };
use crate::model::{Haeufigkeiten, Ordner}; use crate::model::{Haeufigkeiten, Ordner};
use crate::profile::Profile; use crate::profile::Profile;
@@ -192,6 +192,39 @@ impl Sortable for DataForm {
fn sorting_key(&self) -> String { fn sorting_key(&self) -> String {
self.name.clone() self.name.clone()
} }
fn sorted(&mut self) -> &Self {
self.data_catalogues.data_catalogue.sort_unstable();
self.entries
.entry
.sort_unstable_by_key(|item| item.sorting_key());
self.entries.entry.iter_mut().for_each(|item| {
item.sorted();
});
if let Some(ref mut plausibility_rule) = self.plausibility_rules.plausibility_rule {
plausibility_rule.sort_unstable_by_key(|item| item.bezeichnung.clone());
plausibility_rule.iter_mut().for_each(|item| {
if let Some(ref mut data_form_entry_names) = item.data_form_entries.entry_name {
data_form_entry_names.sort_unstable();
}
});
}
self
}
}
impl Comparable for DataForm {
fn get_name(&self) -> String {
self.name.clone()
}
fn get_revision(&self) -> u16 {
self.revision
}
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -423,6 +456,26 @@ impl FormEntry for Entry {
} }
} }
impl Sortable for Entry {
fn sorting_key(&self) -> String {
self.name.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
if let Some(ref mut filter) = self.filter {
if let Some(ref mut ref_entries) = filter.ref_entries {
if let Some(ref mut ref_entry) = ref_entries.ref_entry {
ref_entry.sort_unstable()
}
}
}
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct DataFormEntries { pub struct DataFormEntries {

View File

@@ -24,6 +24,9 @@
use crate::profile::{FormReference, Profile}; use crate::profile::{FormReference, Profile};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
pub mod data_catalogue; pub mod data_catalogue;
pub mod data_form; pub mod data_form;
@@ -236,6 +239,22 @@ pub trait Listable {
pub trait Sortable { pub trait Sortable {
fn sorting_key(&self) -> String; fn sorting_key(&self) -> String;
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
self
}
}
pub trait Comparable: Debug {
fn get_name(&self) -> String;
fn get_revision(&self) -> u16;
fn get_hash(&self) -> String {
let mut h = DefaultHasher::new();
format!("{:?}", self).hash(&mut h);
h.finish().to_string()
}
} }
pub trait FormEntry { pub trait FormEntry {

View File

@@ -1,7 +1,7 @@
/* /*
* MIT License * MIT License
* *
* 2023 Comprehensive Cancer Center Mainfranken * Copyright (c) 2023 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
@@ -25,13 +25,15 @@
use console::style; use console::style;
use quick_xml::de::from_str; use quick_xml::de::from_str;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Debug;
use std::str::FromStr; use std::str::FromStr;
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::unterformular::Unterformular; use crate::model::unterformular::Unterformular;
use crate::model::{FormEntryContainer, Listable, Sortable}; use crate::model::{Comparable, FormEntryContainer, Listable, Sortable};
use crate::profile::Profile; use crate::profile::Profile;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -77,17 +79,142 @@ impl OnkostarEditor {
.property_catalogue .property_catalogue
.sort_unstable_by_key(|e| e.sorting_key()); .sort_unstable_by_key(|e| e.sorting_key());
self.editor.property_catalogue.iter_mut().for_each(|item| {
item.sorted();
});
self.editor self.editor
.data_catalogue .data_catalogue
.sort_unstable_by_key(|e| e.sorting_key()); .sort_unstable_by_key(|e| e.sorting_key());
self.editor.data_catalogue.iter_mut().for_each(|item| {
item.sorted();
});
self.editor self.editor
.data_form .data_form
.sort_unstable_by_key(|e| e.sorting_key()); .sort_unstable_by_key(|e| e.sorting_key());
self.editor.data_form.iter_mut().for_each(|item| {
item.sorted();
});
self.editor self.editor
.unterformular .unterformular
.sort_unstable_by_key(|e| e.sorting_key()); .sort_unstable_by_key(|e| e.sorting_key());
self.editor.unterformular.iter_mut().for_each(|item| {
item.sorted();
});
}
pub fn print_diff(&mut self, other: &mut Self, strict: bool) {
self.sorted();
other.sorted();
Self::print_item_diff(
"Merkmalskataloge",
&self.editor.property_catalogue,
&other.editor.property_catalogue,
strict,
);
Self::print_item_diff(
"Datenkataloge",
&self.editor.data_catalogue,
&other.editor.data_catalogue,
strict,
);
Self::print_item_diff(
"Formulare",
&self.editor.data_form,
&other.editor.data_form,
strict,
);
Self::print_item_diff(
"Unterformulare",
&self.editor.unterformular,
&other.editor.unterformular,
strict,
);
}
fn print_item_diff(
title: &str,
list_a: &[impl Comparable],
list_b: &[impl Comparable],
strict: bool,
) {
println!("\n{}", style(title).underlined());
let mut has_diff = false;
let names_a = list_a
.iter()
.map(|entry| entry.get_name())
.collect::<Vec<_>>();
let names_b = list_b
.iter()
.map(|entry| entry.get_name())
.collect::<Vec<_>>();
names_b.iter().for_each(|entry| {
if !names_a.contains(entry) {
println!("{}: {}", entry, style("Nicht in Datei A enthalten!").red());
has_diff = true;
}
});
names_a.iter().for_each(|entry| {
if !names_b.contains(entry) {
println!("{}: {}", entry, style("Nicht in Datei B enthalten!").red());
has_diff = true;
}
});
list_a.iter().for_each(|entry_a| {
list_b.iter().for_each(|entry_b| {
if entry_a.get_name() == entry_b.get_name() {
match entry_a.get_revision().cmp(&entry_b.get_revision()) {
Ordering::Less => {
println!(
"{}: {} (Revision {} < Revision {})",
entry_a.get_name(),
style("Neuer in Datei B").yellow(),
style(entry_a.get_revision()).blue(),
style(entry_b.get_revision()).green()
);
has_diff = true;
}
Ordering::Greater => {
println!(
"{}: {} (Revision {} > Revision {})",
entry_a.get_name(),
style("Neuer in Datei A").yellow(),
style(entry_a.get_revision()).green(),
style(entry_b.get_revision()).blue()
);
has_diff = true;
}
_ => {
if strict && entry_a.get_hash() != entry_b.get_hash() {
println!(
"{}: {} (z.B. GUID oder Reihenfolge von Unterelementen)",
entry_a.get_name(),
style("Inhaltlich verschieden").yellow()
);
has_diff = true;
} else if strict {
println!("{}: {}", entry_a.get_name(), style("Identisch").green())
}
}
}
}
});
});
if !has_diff {
println!("Keine Unterschiede")
}
} }
} }

View File

@@ -25,7 +25,7 @@
use console::style; use console::style;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::model::{Listable, Ordner, Sortable}; use crate::model::{Comparable, Listable, Ordner, Sortable};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
@@ -67,6 +67,26 @@ impl Sortable for PropertyCatalogue {
fn sorting_key(&self) -> String { fn sorting_key(&self) -> String {
self.name.clone() self.name.clone()
} }
fn sorted(&mut self) -> &Self {
if let Some(ref mut versions) = self.versions.entry {
versions.sort_unstable_by_key(|item| item.version_number);
versions.iter_mut().for_each(|version| {
version.sorted();
});
}
self
}
}
impl Comparable for PropertyCatalogue {
fn get_name(&self) -> String {
self.name.clone()
}
fn get_revision(&self) -> u16 {
self.revision
}
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -105,6 +125,40 @@ pub struct Version {
categories: Categories, categories: Categories,
} }
impl Sortable for Version {
fn sorting_key(&self) -> String {
self.oid.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
if let Some(ref mut abbildung) = self.abbildung {
abbildung.sort_unstable_by_key(|item| item.sorting_key());
abbildung.iter_mut().for_each(|item| {
item.sorted();
});
}
self.entries
.content
.sort_unstable_by_key(|item| item.sorting_key());
self.entries.content.iter_mut().for_each(|item| {
item.sorted();
});
self.categories
.content
.sort_unstable_by_key(|item| item.sorting_key());
self.categories.content.iter_mut().for_each(|item| {
item.sorted();
});
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct VersionEntries { pub struct VersionEntries {
@@ -131,6 +185,12 @@ pub struct VersionEntry {
position: String, position: String,
} }
impl Sortable for VersionEntry {
fn sorting_key(&self) -> String {
self.code.clone()
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Categories { pub struct Categories {
@@ -155,6 +215,26 @@ pub struct Category {
category_entries: CategoryEntries, category_entries: CategoryEntries,
} }
impl Sortable for Category {
fn sorting_key(&self) -> String {
self.name.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
self.category_entries
.content
.sort_unstable_by_key(|item| item.sorting_key());
self.category_entries.content.iter_mut().for_each(|item| {
item.sorted();
});
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct CategoryEntries { pub struct CategoryEntries {
@@ -181,6 +261,12 @@ pub struct CategoryEntry {
note: Option<String>, note: Option<String>,
} }
impl Sortable for CategoryEntry {
fn sorting_key(&self) -> String {
self.code.clone()
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Abbildung { pub struct Abbildung {
@@ -190,6 +276,24 @@ pub struct Abbildung {
content: Vec<AbbildungEintrag>, content: Vec<AbbildungEintrag>,
} }
impl Sortable for Abbildung {
fn sorting_key(&self) -> String {
self.ziel_mk_version_oid.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
self.content.sort_unstable_by_key(|item| item.sorting_key());
self.content.iter_mut().for_each(|item| {
item.sorted();
});
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct AbbildungEintrag { pub struct AbbildungEintrag {
@@ -199,6 +303,12 @@ pub struct AbbildungEintrag {
entry_to: AbbildungEntry, entry_to: AbbildungEntry,
} }
impl Sortable for AbbildungEintrag {
fn sorting_key(&self) -> String {
format!("{}-{}", self.entry_from.code, self.entry_to.code)
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct AbbildungEntry { pub struct AbbildungEntry {

View File

@@ -26,8 +26,8 @@ use console::style;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::model::{ use crate::model::{
apply_profile_to_form_entry, Ansichten, Entries, Filter, FormEntry, FormEntryContainer, apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
Listable, MenuCategory, PlausibilityRules, Script, Sortable, FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
}; };
use crate::model::{Haeufigkeiten, Ordner}; use crate::model::{Haeufigkeiten, Ordner};
use crate::profile::Profile; use crate::profile::Profile;
@@ -211,6 +211,39 @@ impl Sortable for Unterformular {
fn sorting_key(&self) -> String { fn sorting_key(&self) -> String {
self.name.clone() self.name.clone()
} }
fn sorted(&mut self) -> &Self {
self.data_catalogues.data_catalogue.sort_unstable();
self.entries
.entry
.sort_unstable_by_key(|item| item.sorting_key());
self.entries.entry.iter_mut().for_each(|item| {
item.sorted();
});
if let Some(ref mut plausibility_rule) = self.plausibility_rules.plausibility_rule {
plausibility_rule.sort_unstable_by_key(|item| item.bezeichnung.clone());
plausibility_rule.iter_mut().for_each(|item| {
if let Some(ref mut data_form_entry_names) = item.data_form_entries.entry_name {
data_form_entry_names.sort_unstable();
}
});
}
self
}
}
impl Comparable for Unterformular {
fn get_name(&self) -> String {
self.name.clone()
}
fn get_revision(&self) -> u16 {
self.revision
}
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -443,6 +476,26 @@ impl FormEntry for Entry {
} }
} }
impl Sortable for Entry {
fn sorting_key(&self) -> String {
self.name.clone()
}
fn sorted(&mut self) -> &Self
where
Self: Sized,
{
if let Some(ref mut filter) = self.filter {
if let Some(ref mut ref_entries) = filter.ref_entries {
if let Some(ref mut ref_entry) = ref_entries.ref_entry {
ref_entry.sort_unstable()
}
}
}
self
}
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct DataFormEntries { pub struct DataFormEntries {