mirror of
https://github.com/pcvolkmer/osc-variant.git
synced 2025-04-19 19:56:50 +00:00
Add subcommand 'diff' to compare two OSC files
This commit is contained in:
parent
b04ee563f2
commit
b030ce6a53
14
README.md
14
README.md
@ -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 list meine-beispieldatei.osc andere-beispieldatei.osc
|
||||||
|
```
|
||||||
|
|
||||||
|
bzw.
|
||||||
|
|
||||||
|
```
|
||||||
|
osc-variant list meine-beispieldatei.osc andere-beispieldatei.osc --strict
|
||||||
|
```
|
||||||
|
|
||||||
Zum Anpassen des Inhalts einer Datei:
|
Zum Anpassen des Inhalts einer Datei:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -53,4 +53,11 @@ 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,
|
||||||
},
|
},
|
||||||
|
#[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,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
17
src/main.rs
17
src/main.rs
@ -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;
|
||||||
|
|
||||||
@ -157,6 +158,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(())
|
||||||
|
@ -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)]
|
||||||
@ -72,6 +72,16 @@ impl Sortable for DataCatalogue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Entries {
|
pub struct Entries {
|
||||||
|
@ -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;
|
||||||
@ -194,6 +194,16 @@ impl Sortable for DataForm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct DataCatalogues {
|
pub struct DataCatalogues {
|
||||||
|
@ -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;
|
||||||
@ -238,6 +241,16 @@ pub trait Sortable {
|
|||||||
fn sorting_key(&self) -> String;
|
fn sorting_key(&self) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
fn get_name(&self) -> String;
|
fn get_name(&self) -> String;
|
||||||
fn get_type(&self) -> String;
|
fn get_type(&self) -> String;
|
||||||
|
@ -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)]
|
||||||
@ -89,6 +91,115 @@ impl OnkostarEditor {
|
|||||||
.unterformular
|
.unterformular
|
||||||
.sort_unstable_by_key(|e| e.sorting_key());
|
.sort_unstable_by_key(|e| e.sorting_key());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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. 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for OnkostarEditor {
|
impl FromStr for OnkostarEditor {
|
||||||
|
@ -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)]
|
||||||
@ -69,6 +69,16 @@ impl Sortable for PropertyCatalogue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct Versions {
|
pub struct Versions {
|
||||||
|
@ -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;
|
||||||
@ -213,6 +213,16 @@ impl Sortable for Unterformular {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct DataCatalogues {
|
pub struct DataCatalogues {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user