diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..22d21be --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,331 @@ +/* + * 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::error::Error; +use std::fs; +use std::fs::OpenOptions; +use std::io::Write; +use std::ops::Add; +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +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, 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(()) +} + +fn read_profile(filename: String) -> Result { + 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) +} + +pub fn handle(command: SubCommand) -> Result<(), Box> { + 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(format!("{}", 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(format!("{}", file.filename())).bold(), + style(" ignoriert").yellow() + ); + } + } + } + } + InputFile::Profile { 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::Profile { 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('.') { + read_profile(profile.clone()).map_err(|_| { + FileError::Reading(profile, "Kann Profildatei nicht lesen!".into()) + })? + } 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 = &"\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 OnkostarEditor = + &mut InputFile::read(inputfile_a, None)?.try_into()?; + let data_b: &mut OnkostarEditor = + &mut InputFile::read(inputfile_b, None)?.try_into()?; + + 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(()) +} diff --git a/src/main.rs b/src/main.rs index bd95079..0c3524c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,321 +22,22 @@ * 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 std::str::FromStr; - +use crate::cli::Cli; +use crate::commands::handle; use clap::Parser; -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::{Cli, SubCommand}; -use crate::file_io::{FileError, InputFile}; -use crate::model::onkostar_editor::OnkostarEditor; -use crate::profile::Profile; +use std::error::Error; mod checks; mod cli; +mod commands; mod file_io; mod model; mod profile; #[cfg(feature = "unzip-osb")] mod unzip_osb; -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 { - 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> { let cli = Cli::parse(); - - match cli.cmd { - 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(format!("{}", 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(format!("{}", file.filename())).bold(), - style(" ignoriert").yellow() - ); - } - } - } - } - InputFile::Profile { 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::Profile { 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('.') { - read_profile(profile.clone()).map_err(|_| { - FileError::Reading(profile, "Kann Profildatei nicht lesen!".into()) - })? - } 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 = &"\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 OnkostarEditor = - &mut InputFile::read(inputfile_a, None)?.try_into()?; - let data_b: &mut OnkostarEditor = - &mut InputFile::read(inputfile_b, None)?.try_into()?; - - 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()), - } - } - }; - + handle(cli.cmd)?; Ok(()) }