From 46d4156bb6044f221382b8cdd6b417d3a81a37b4 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Sun, 7 Dec 2025 12:59:55 +0100 Subject: [PATCH] feat: add more verbose output (#63) Using -v the list and tree sub-command will show hash value based on catalogue or form contents. --- README.md | 6 +++++ src/cli.rs | 3 +++ src/commands.rs | 32 +++++++++++++---------- src/main.rs | 2 +- src/model/data_catalogue.rs | 8 ++++-- src/model/form.rs | 13 ++++++---- src/model/mod.rs | 24 ++++++++++++----- src/model/onkostar_editor.rs | 50 +++++++++++++++++++----------------- src/model/requirements.rs | 8 ++++-- 9 files changed, 92 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 5b2c36e..51c22da 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,9 @@ Mit der Option `--filter` kann die Ausgabe eingeschränkt werden. *Bei Verwendung der OSB-Funktionalität kann die Eingabe eines Passworts erforderlich sein.* +Die Option `-v` sorgt dafür, dass die eine Prüfsumme für Kataloge und Formulare berechnet und angezeigt wird. +Dadurch können inhaltliche Unterschiede bei identischer Revisionsnummer erkannt werden. + #### Unterbefehl `tree` Zum Auflisten der Inhalte mit allen Abhängigkeiten, z.B. Daten- und Merkmalskataloge und bei Formularen wird der Befehl @@ -87,6 +90,9 @@ Achtung! Dies erzeugt eine sehr umfangreiche Ausgabe. Mit der Option `--filter` kann auch hier die Ausgabe eingeschränkt werden. +Wie bei `list` sorgt auch hier die Option `-v` dafür, +dass die eine Prüfsumme für Kataloge und Formulare berechnet und angezeigt wird. + #### Unterbefehl `diff` Zum Vergleich zweier OSC-Dateien wird der Unterbefehl `diff` verwendet. diff --git a/src/cli.rs b/src/cli.rs index 252871c..3c58082 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -27,6 +27,9 @@ use clap_complete::Shell; pub struct Cli { #[command(subcommand)] pub cmd: SubCommand, + + #[arg(short = 'v', global = true, help = "Zeige umfangreichere Ausgaben")] + pub verbose: bool, } #[derive(Subcommand)] diff --git a/src/commands.rs b/src/commands.rs index fda1135..fd4a4a5 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -17,13 +17,13 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -use crate::checks::{check_file, print, CheckNotice}; +use crate::checks::{CheckNotice, check_file, print}; use crate::cli::{Cli, SubCommand}; use crate::file_io::{FileError, FileReader, InputFile}; use crate::model::onkostar_editor::OnkostarEditor; use crate::profile::Profile; use clap::CommandFactory; -use clap_complete::{generate, Shell}; +use clap_complete::{Shell, generate}; use console::style; use quick_xml::se::Serializer; use serde::Serialize; @@ -35,19 +35,19 @@ use std::io::Write; use std::ops::Add; use std::path::{Path, PathBuf}; -pub fn handle(command: SubCommand) -> Result<(), Box> { +pub fn handle(command: SubCommand, verbose: bool) -> Result<(), Box> { match command { SubCommand::Completion { shell } => handle_completion(shell), SubCommand::List { inputfile, sorted, filter, - } => handle_list(inputfile, sorted, filter)?, + } => handle_list(inputfile, sorted, filter, verbose)?, SubCommand::Tree { inputfile, sorted, filter, - } => handle_tree(inputfile, sorted, filter)?, + } => handle_tree(inputfile, sorted, filter, verbose)?, SubCommand::Modify { inputfile, profile, @@ -106,6 +106,7 @@ fn handle_list( inputfile: String, sorted: bool, filter: Option, + verbose: bool, ) -> Result<(), Box> { match InputFile::read(inputfile, None)? { osc @ InputFile::Osc { .. } => { @@ -114,10 +115,10 @@ fn handle_list( content.sorted(); } if let Some(name) = filter { - OnkostarEditor::print_list_filtered(&mut content, name.as_str()); + OnkostarEditor::print_list_filtered(&mut content, name.as_str(), verbose); return Ok(()); } - content.print_list(); + content.print_list(verbose); } InputFile::Osb { content, .. } => { for file in content { @@ -141,10 +142,14 @@ fn handle_list( content.sorted(); } if let Some(name) = filter { - OnkostarEditor::print_list_filtered(&mut content, name.as_str()); + OnkostarEditor::print_list_filtered( + &mut content, + name.as_str(), + verbose, + ); return Ok(()); } - content.print_list(); + content.print_list(verbose); println!(); } _ => { @@ -172,6 +177,7 @@ fn handle_tree( inputfile: String, sorted: bool, filter: Option, + verbose: bool, ) -> Result<(), Box> { match InputFile::read(inputfile, None)? { osc @ InputFile::Osc { .. } => { @@ -180,10 +186,10 @@ fn handle_tree( content.sorted(); } if let Some(name) = filter { - OnkostarEditor::print_tree_filtered(&mut content, name.as_str()); + OnkostarEditor::print_tree_filtered(&mut content, name.as_str(), verbose); return Ok(()); } - OnkostarEditor::print_tree(&content); + OnkostarEditor::print_tree(&content, verbose); } InputFile::Osb { filename, .. } => { return Err(Box::new(FileError::Reading( @@ -248,9 +254,7 @@ fn handle_modify( let output = &"\n" .to_string() - .add( - buf.as_str(), - ); + .add(buf.as_str()); match outputfile { Some(filename) => write_outputfile(filename, output)?, diff --git a/src/main.rs b/src/main.rs index 669b34d..06ce553 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,6 @@ mod unzip_osb; fn main() -> Result<(), Box> { let cli = Cli::parse(); - handle(cli.cmd)?; + handle(cli.cmd, cli.verbose)?; Ok(()) } diff --git a/src/model/data_catalogue.rs b/src/model/data_catalogue.rs index 1ce0001..68cda31 100644 --- a/src/model/data_catalogue.rs +++ b/src/model/data_catalogue.rs @@ -121,10 +121,14 @@ impl Requires for DataCatalogue { result } - fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String { + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor, verbose: bool) -> String { format!( "{}\n{}", - self.to_listed_string(), + if verbose { + self.to_verbose_listed_string() + } else { + self.to_listed_string() + }, self.get_required_entries(all) .iter() .filter_map(|entry| match entry { diff --git a/src/model/form.rs b/src/model/form.rs index a8c5c60..543292d 100644 --- a/src/model/form.rs +++ b/src/model/form.rs @@ -23,9 +23,9 @@ use crate::model::onkostar_editor::OnkostarEditor; use crate::model::other::Entry; use crate::model::requirements::{Requirement, Requires}; use crate::model::{ - apply_profile_to_form_entry, apply_profile_to_form_field, Ansichten, Comparable, Entries, FolderContent, FormEntry, - FormEntryContainer, Kennzahlen, Listable, MenuCategory, PlausibilityRules, PunkteKategorien, - Script, Sortable, + Ansichten, Comparable, Entries, FolderContent, FormEntry, FormEntryContainer, Kennzahlen, + Listable, MenuCategory, PlausibilityRules, PunkteKategorien, Script, Sortable, + apply_profile_to_form_entry, apply_profile_to_form_field, }; use crate::model::{Haeufigkeiten, Ordner}; use crate::profile::Profile; @@ -250,7 +250,10 @@ impl FormEntryContainer for Form { } } -impl Listable for Form { +impl Listable for Form +where + Form: Comparable, +{ fn to_listed_string(&self) -> String { format!( "{} ({}) '{}' in Revision '{}'", @@ -673,8 +676,8 @@ pub struct DataFormEntries { mod tests { use std::str::FromStr; - use crate::model::onkostar_editor::OnkostarEditor; use crate::model::Script; + use crate::model::onkostar_editor::OnkostarEditor; use crate::profile::Profile; #[test] diff --git a/src/model/mod.rs b/src/model/mod.rs index 675f225..bb6ad28 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -18,16 +18,15 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +use crate::model::requirements::Requires; +use crate::profile::{FormField, FormReference, Profile, WithScriptsCode}; +use console::style; +use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::hash_map::DefaultHasher; use std::fmt::Debug; use std::hash::{Hash, Hasher}; -use serde::{Deserialize, Serialize}; - -use crate::model::requirements::Requires; -use crate::profile::{FormField, FormReference, Profile, WithScriptsCode}; - pub mod data_catalogue; pub mod form; pub mod onkostar_editor; @@ -354,8 +353,19 @@ pub trait FormEntryContainer { fn apply_profile(&mut self, profile: &Profile); } -pub trait Listable { +pub trait Listable +where + Self: Comparable, +{ fn to_listed_string(&self) -> String; + + fn to_verbose_listed_string(&self) -> String { + format!( + "{} {}", + self.to_listed_string(), + style(format!("[{}]", &self.get_hash()[..7])).dim() + ) + } } pub trait Sortable { @@ -375,7 +385,7 @@ pub trait Comparable: Debug { fn get_hash(&self) -> String { let mut h = DefaultHasher::new(); format!("{self:?}").hash(&mut h); - h.finish().to_string() + format!("{:x}", h.finish()) } fn compare_by_requirement(_: &Self, _: &Self) -> Ordering where diff --git a/src/model/onkostar_editor.rs b/src/model/onkostar_editor.rs index bc7ae29..b8bafb0 100644 --- a/src/model/onkostar_editor.rs +++ b/src/model/onkostar_editor.rs @@ -114,17 +114,17 @@ impl OnkostarEditor { }); } - pub fn print_list(&self) { + pub fn print_list(&self, verbose: bool) { 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("Datenkataloge", &self.editor.data_catalogue); - Self::print_items("Formulare", &self.editor.data_form); - Self::print_items("Unterformulare", &self.editor.unterformular); + Self::print_items("Merkmalskataloge", &self.editor.property_catalogue, verbose); + Self::print_items("Datenkataloge", &self.editor.data_catalogue, verbose); + Self::print_items("Formulare", &self.editor.data_form, verbose); + Self::print_items("Unterformulare", &self.editor.unterformular, verbose); } fn filter_by_name_contains(&mut self, name: &str) { @@ -141,7 +141,7 @@ impl OnkostarEditor { .unterformular .retain(|e| e.get_name().contains(name)); } - pub fn print_list_filtered(&mut self, name: &str) { + pub fn print_list_filtered(&mut self, name: &str, verbose: bool) { println!( "Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte für '{}' sind gespeichert", style(&self.info_xml.datum_xml).yellow(), @@ -152,24 +152,28 @@ impl OnkostarEditor { 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); + Self::print_items("Merkmalskataloge", &self.editor.property_catalogue, verbose); + Self::print_items("Datenkataloge", &self.editor.data_catalogue, verbose); + Self::print_items("Formulare", &self.editor.data_form, verbose); + Self::print_items("Unterformulare", &self.editor.unterformular, verbose); } - fn print_items(title: &str, list: &[impl Listable]) { + fn print_items(title: &str, list: &[impl Listable], verbose: bool) { print!("\n{} {}", list.len(), style(title).underlined()); println!( " - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert", style("S").yellow() ); for entry in list { + if verbose { + println!("{}", entry.to_verbose_listed_string()); + continue; + } println!("{}", entry.to_listed_string()); } } - pub fn print_tree(&self) { + pub fn print_tree(&self, verbose: bool) { println!( "Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte sind gespeichert", style(&self.info_xml.datum_xml).yellow(), @@ -177,13 +181,13 @@ impl OnkostarEditor { 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); + Self::print_items("Merkmalskataloge", &self.editor.property_catalogue, verbose); + self.print_items_tree("Datenkataloge", &self.editor.data_catalogue, verbose); + self.print_items_tree("Formulare", &self.editor.data_form, verbose); + self.print_items_tree("Unterformulare", &self.editor.unterformular, verbose); } - pub fn print_tree_filtered(&mut self, name: &str) { + pub fn print_tree_filtered(&mut self, name: &str, verbose: bool) { println!( "Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte für '{}' sind gespeichert", style(&self.info_xml.datum_xml).yellow(), @@ -194,20 +198,20 @@ impl OnkostarEditor { 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); + Self::print_items("Merkmalskataloge", &self.editor.property_catalogue, verbose); + self.print_items_tree("Datenkataloge", &self.editor.data_catalogue, verbose); + self.print_items_tree("Formulare", &self.editor.data_form, verbose); + self.print_items_tree("Unterformulare", &self.editor.unterformular, verbose); } - fn print_items_tree(&self, title: &str, list: &[impl Requires]) { + fn print_items_tree(&self, title: &str, list: &[impl Requires], verbose: bool) { print!("\n{} {}", list.len(), style(title).underlined()); println!( " - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert", style("S").yellow() ); for entry in list { - println!("{}", entry.to_requirement_string(self)); + println!("{}", entry.to_requirement_string(self, verbose)); } } diff --git a/src/model/requirements.rs b/src/model/requirements.rs index 7db6c3c..4deafa6 100644 --- a/src/model/requirements.rs +++ b/src/model/requirements.rs @@ -110,10 +110,14 @@ where fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec>; - fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String { + fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor, verbose: bool) -> String { format!( "{}\n{}", - self.to_listed_string(), + if verbose { + self.to_verbose_listed_string() + } else { + self.to_listed_string() + }, self.get_required_entries(all) .iter() .filter_map(|entry| match entry {