use std::collections::linked_list::LinkedList;
use crate::{
action::{
CryptographicAction,
Direction,
},
ui::application::{
ApplicationError,
OPTION_CIPHER,
},
cipher::{
Cipher,
shift_cipher::{
CaesarContext,
VigenereContext,
},
},
};
#[cfg(feature = "caesar")]
const ALGORITHM_CAESAR: &str = "CAESAR";
#[cfg(feature = "vigenere")]
const ALGORITHM_VIGENERE: &str = "VIGENERE";
const OPTION_DECRYPT: &str = "decrypt";
const OPTION_ENCRYPT: &str = "encrypt";
const OPTION_KEY: &str = "key";
const OPTION_LOWERCASE: &str = "lowercase";
const OPTION_UPPERCASE: &str = "uppercase";
#[inline]
fn create_new_cipher_context(
arguments: Option<&String>
) -> Result<(Box<dyn Cipher>, String), ApplicationError> {
let arguments = if let Some(arg) = arguments {
arg
} else {
let mut description = "Missing argument for ".to_string();
description.push_str(OPTION_CIPHER);
description.push_str("!");
return Err(ApplicationError::MissingArgument(
description, "create_new_cipher_context()".to_string(),
));
};
let arguments = arguments
.split(":")
.collect::<Vec<&str>>();
if arguments.len() == 1 {
let mut description = "For algorithm class ".to_string();
description.push_str(OPTION_CIPHER);
description.push_str("!");
Err(ApplicationError::MissingArgument(
description, "create_new_cipher_context()".to_string(),
))
} else if arguments.len() > 2 {
let mut description = "For algorithm class ".to_string();
description.push_str(OPTION_CIPHER);
description.push_str("!");
Err(ApplicationError::TooManyArguments(
description, "create_new_cipher_context()".to_string(),
))
} else {
let ctx: Box<dyn Cipher> = match arguments[0] {
#[cfg(feature = "caesar")]
algorithm if algorithm == ALGORITHM_CAESAR => {
Box::from(CaesarContext::new())
}
#[cfg(any(feature = "vigenere"))]
algorithm if algorithm == ALGORITHM_VIGENERE => {
Box::from(VigenereContext::new())
}
algorithm => {
let mut description = "Algorithm ".to_string();
description.push_str(algorithm);
description.push_str(" unknown!");
return Err(ApplicationError::WrongCommandLineArgument(
description,
"create_new_cipher_context()".to_string(),
));
}
};
Ok((ctx, arguments[1].to_string()))
}
}
pub(crate) fn handle_cipher_option_arguments(
arguments: Option<&String>,
current_action: &mut Option<CryptographicAction>,
) -> Result<(), ApplicationError> {
let (mut ctx, arguments) = match create_new_cipher_context(
arguments
) {
Ok(ctx) => { ctx }
Err(error) => { return Err(error); }
};
let mut arguments = split_cipher_algorithm_arguments(&arguments);
let mut direction = None;
let mut maybe_arg = arguments.pop_front();
let mut case_argument_encountered = false;
while maybe_arg != None {
let code_arg = maybe_arg.unwrap();
match code_arg {
arg if arg == OPTION_DECRYPT || arg == OPTION_ENCRYPT => {
if direction.is_none() {
direction = Some(
if arg == OPTION_DECRYPT {
Direction::ToPlain
} else {
Direction::ToCipher
}
);
} else {
return Err(ApplicationError::TooManyArguments(
"Only exactly one of \"encrypt\" and \"decrypt\" may be \
supplied!".to_string(),
"handle_cipher_option_arguments()".to_string(),
));
}
}
arg if arg == OPTION_KEY => {
let key = if let Some(value) = arguments.pop_front() {
Vec::from(value)
} else {
let mut description = "Missing value for ".to_string();
description.push_str(ctx.get_atom_type().to_string().as_str());
description.push_str(" key!");
return Err(ApplicationError::MissingArgument(
description,
"handle_cipher_option_arguments()".to_string(),
));
};
if let Err(error) = ctx.set_key(&key) {
let mut description = "The following CryptoError has occurred while \
setting the key for a ".to_string();
description.push_str(ctx.get_atom_type().to_string().as_str());
description.push_str(" cipher!");
return Err(ApplicationError::MalformedKey(
description,
"handle_cipher_option_arguments()".to_string(),
error,
));
}
}
arg if arg == OPTION_LOWERCASE || arg == OPTION_UPPERCASE => {
if case_argument_encountered {
return Err(ApplicationError::TooManyArguments(
"Only exactly one of \"uppercase\" and \"lowercase\" may be \
supplied!".to_string(),
"handle_cipher_option_arguments()".to_string(),
));
} else {
case_argument_encountered = true;
}
ctx.set_ciphertext_capitalization(arg == OPTION_UPPERCASE);
}
arg => {
let mut description = "For algorithm ".to_string();
description.push_str(ctx.get_atom_type().to_string().as_str());
description.push_str("! \"");
description.push_str(arg);
description.push_str("\" is unknown!");
return Err(ApplicationError::WrongCommandLineArgument(
description,
"handle_cipher_option_arguments()".to_string(),
));
}
}
maybe_arg = arguments.pop_front();
}
if let Some(direction) = direction {
*current_action = Some(CryptographicAction::new_cipher_action(
ctx, direction,
));
Ok(())
} else {
Err(ApplicationError::MissingArgument(
"Neither \"encrypt\" nor \"decrypt\" argument has been supplied!".to_string(),
"handle_cipher_option_arguments()".to_string(),
))
}
}
#[inline]
fn split_cipher_algorithm_arguments(
arguments: &String,
) -> LinkedList<&str> {
arguments
.as_str()
.split(",")
.collect::<LinkedList<&str>>()
}