From 103075ab78fa9c4286d19726c860ba4070f68b65 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Tue, 29 Aug 2023 17:34:02 +0200 Subject: [PATCH] Add sub command 'tree' to show dependencies --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 9 ++++++ src/cli.rs | 9 ++++++ src/main.rs | 7 +++++ src/model/data_catalogue.rs | 43 ++++++++++++++++++++++++++++ src/model/data_form.rs | 54 ++++++++++++++++++++++++++++++++++++ src/model/mod.rs | 7 +++-- src/model/onkostar_editor.rs | 54 ++++++++++++++++++++++++++++++++++-- src/model/requirements.rs | 38 +++++++++++++++++++++++++ src/model/unterformular.rs | 54 ++++++++++++++++++++++++++++++++++++ 11 files changed, 272 insertions(+), 7 deletions(-) create mode 100644 src/model/requirements.rs diff --git a/Cargo.lock b/Cargo.lock index 6e5b537..5568e34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,7 +126,7 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "osc-variant" -version = "0.2.0" +version = "0.3.0" dependencies = [ "clap", "console", diff --git a/Cargo.toml b/Cargo.toml index 8a93545..49f21bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "osc-variant" -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = ["Paul-Christian Volkmer "] description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort" diff --git a/README.md b/README.md index 3c3fea8..b45418f 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,15 @@ Zum Auflisten der Inhalte einer Datei wird folgender Befehl verwendet: osc-variant list meine-beispieldatei.osc ``` +Zum Auflisten der Inhalte mit allen Abhängigkeiten, z.B. Daten- und Merkmalkataloge und bei Formularen wird der Befehl +`tree` verwendet: + +``` +osc-variant tree meine-beispieldatei.osc +``` + +Achtung! Dies erzeugt eine sehr umfangreiche Ausgabe. + 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. diff --git a/src/cli.rs b/src/cli.rs index 2a3d6fb..c99d78b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -43,6 +43,15 @@ pub enum Command { )] sorted: bool, }, + #[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, + }, #[command(about = "Modifiziert die angegebene Datei anhand der Profildatei")] Modify { inputfile: String, diff --git a/src/main.rs b/src/main.rs index d9dde7d..15fa905 100644 --- a/src/main.rs +++ b/src/main.rs @@ -117,6 +117,13 @@ fn main() -> Result<(), Box> { } data.print_list(); } + Command::Tree { inputfile, sorted } => { + let mut data = read_inputfile(inputfile)?; + if sorted { + data.sorted() + } + OnkostarEditor::print_tree(&data); + } Command::Modify { inputfile, profile, diff --git a/src/model/data_catalogue.rs b/src/model/data_catalogue.rs index 1c98b2b..231be2e 100644 --- a/src/model/data_catalogue.rs +++ b/src/model/data_catalogue.rs @@ -22,9 +22,13 @@ * SOFTWARE. */ +use std::collections::HashSet; + use console::style; use serde::{Deserialize, Serialize}; +use crate::model::onkostar_editor::OnkostarEditor; +use crate::model::requirements::{Requirement, Requires}; use crate::model::{Comparable, Listable, Ordner, Sortable}; #[derive(Serialize, Deserialize, Debug)] @@ -92,6 +96,45 @@ impl Comparable for DataCatalogue { } } +impl Requires for DataCatalogue { + fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec { + 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::>() + .into_iter() + .map(|entry| all.find_property_catalogue(entry.as_str())) + .filter(Option::is_some) + .map(|entry| Requirement::PropertyCatalogue(entry.unwrap())) + .collect::>() + } + + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String { + format!( + "Datenkatalog '{}' in Revision '{}'\n{}", + style(&self.name).yellow(), + style(&self.revision).yellow(), + self.get_required_entries(all) + .iter() + .map(|entry| match entry { + Requirement::PropertyCatalogue(x) => { + Some(format!(" + {}\n", x.to_listed_string())) + } + _ => None, + }) + .filter(Option::is_some) + .flatten() + .collect::>() + .join("") + ) + } +} + #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct Entries { diff --git a/src/model/data_form.rs b/src/model/data_form.rs index b545258..27cab99 100644 --- a/src/model/data_form.rs +++ b/src/model/data_form.rs @@ -22,9 +22,13 @@ * SOFTWARE. */ +use std::collections::HashSet; + use console::style; use serde::{Deserialize, Serialize}; +use crate::model::onkostar_editor::OnkostarEditor; +use crate::model::requirements::{Requirement, Requires}; use crate::model::{ apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry, FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable, @@ -227,6 +231,56 @@ impl Comparable for DataForm { } } +impl Requires for DataForm { + fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec { + self.data_catalogues + .data_catalogue + .iter() + .collect::>() + .into_iter() + .map(|entry| all.find_data_catalogue(entry.as_str())) + .filter(Option::is_some) + .map(|entry| Requirement::DataCatalogue(entry.unwrap())) + .collect::>() + } + + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String { + format!( + "Formular '{}' in Revision '{}'\n{}", + style(&self.name).yellow(), + style(&self.revision).yellow(), + 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(y) => Some(y.to_listed_string()), + _ => None, + }) + .filter(Option::is_some) + .map(|item| format!(" - {}\n", item.unwrap())) + .collect::>() + .join(""); + + if inner.is_empty() { + Some(format!(" + {}\n", x.to_listed_string())) + } else { + Some(format!(" + {}\n{}", x.to_listed_string(), inner)) + } + } + _ => None, + }) + .filter(Option::is_some) + .flatten() + .collect::>() + .join("") + ) + } +} + #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct DataCatalogues { diff --git a/src/model/mod.rs b/src/model/mod.rs index bb20d0d..37865cf 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -22,16 +22,19 @@ * SOFTWARE. */ -use crate::profile::{FormReference, Profile}; -use serde::{Deserialize, Serialize}; use std::collections::hash_map::DefaultHasher; use std::fmt::Debug; use std::hash::{Hash, Hasher}; +use serde::{Deserialize, Serialize}; + +use crate::profile::{FormReference, Profile}; + pub mod data_catalogue; pub mod data_form; pub mod onkostar_editor; pub mod property_catalogue; +pub mod requirements; pub mod unterformular; #[derive(Serialize, Deserialize, Debug)] diff --git a/src/model/onkostar_editor.rs b/src/model/onkostar_editor.rs index 914856a..6d78f55 100644 --- a/src/model/onkostar_editor.rs +++ b/src/model/onkostar_editor.rs @@ -22,16 +22,18 @@ * SOFTWARE. */ -use console::style; -use quick_xml::de::from_str; -use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt::Debug; use std::str::FromStr; +use console::style; +use quick_xml::de::from_str; +use serde::{Deserialize, Serialize}; + use crate::model::data_catalogue::DataCatalogue; use crate::model::data_form::DataForm; use crate::model::property_catalogue::PropertyCatalogue; +use crate::model::requirements::Requires; use crate::model::unterformular::Unterformular; use crate::model::{Comparable, FormEntryContainer, Listable, Sortable}; use crate::profile::Profile; @@ -46,6 +48,32 @@ pub struct 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 apply_profile(&mut self, profile: &Profile) { self.editor.data_form.iter_mut().for_each(|data_form| { data_form.apply_profile(profile); @@ -74,6 +102,26 @@ impl OnkostarEditor { .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); + } + + fn print_items_tree(&self, title: &str, list: &[impl Requires]) { + println!("\n{} {}", list.len(), style(title).underlined()); + list.iter() + .for_each(|entry| println!("{}", entry.to_requirement_string(self))); + } + pub fn sorted(&mut self) { self.editor .property_catalogue diff --git a/src/model/requirements.rs b/src/model/requirements.rs new file mode 100644 index 0000000..c74659f --- /dev/null +++ b/src/model/requirements.rs @@ -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 crate::model::data_catalogue::DataCatalogue; +use crate::model::onkostar_editor::OnkostarEditor; +use crate::model::property_catalogue::PropertyCatalogue; + +pub enum Requirement<'a> { + PropertyCatalogue(&'a PropertyCatalogue), + DataCatalogue(&'a DataCatalogue), +} + +pub trait Requires { + fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec; + + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String; +} diff --git a/src/model/unterformular.rs b/src/model/unterformular.rs index 8d37472..be8c721 100644 --- a/src/model/unterformular.rs +++ b/src/model/unterformular.rs @@ -22,9 +22,13 @@ * SOFTWARE. */ +use std::collections::HashSet; + use console::style; use serde::{Deserialize, Serialize}; +use crate::model::onkostar_editor::OnkostarEditor; +use crate::model::requirements::{Requirement, Requires}; use crate::model::{ apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry, FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable, @@ -246,6 +250,56 @@ impl Comparable for Unterformular { } } +impl Requires for Unterformular { + fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec { + self.data_catalogues + .data_catalogue + .iter() + .collect::>() + .into_iter() + .map(|entry| all.find_data_catalogue(entry.as_str())) + .filter(Option::is_some) + .map(|entry| Requirement::DataCatalogue(entry.unwrap())) + .collect::>() + } + + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String { + format!( + "Unterformular '{}' in Revision '{}'\n{}", + style(&self.name).yellow(), + style(&self.revision).yellow(), + 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(y) => Some(y.to_listed_string()), + _ => None, + }) + .filter(Option::is_some) + .map(|item| format!(" - {}\n", item.unwrap())) + .collect::>() + .join(""); + + if inner.is_empty() { + Some(format!(" + {}\n", x.to_listed_string())) + } else { + Some(format!(" + {}\n{}", x.to_listed_string(), inner)) + } + } + _ => None, + }) + .filter(Option::is_some) + .flatten() + .collect::>() + .join("") + ) + } +} + #[derive(Serialize, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct DataCatalogues {