From 6b0f1c72be9960be3a62e5c3939e969b451ebef0 Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 22 Sep 2020 13:47:01 +0100 Subject: [PATCH] generate parsing --- src/args/error.rs | 5 + src/args/mod.rs | 4 +- src/args/parse.rs | 371 ------------------------------------- src/args/parse/generate.rs | 166 +++++++++++++++++ src/args/parse/mod.rs | 257 +++++++++++++++++++++++++ src/args/parse/normal.rs | 203 ++++++++++++++++++++ src/config.rs | 20 +- 7 files changed, 643 insertions(+), 383 deletions(-) delete mode 100644 src/args/parse.rs create mode 100644 src/args/parse/generate.rs create mode 100644 src/args/parse/mod.rs create mode 100644 src/args/parse/normal.rs diff --git a/src/args/error.rs b/src/args/error.rs index 6805fc1..a56928e 100644 --- a/src/args/error.rs +++ b/src/args/error.rs @@ -10,7 +10,9 @@ use std::{ pub enum ParseErrorKind { ExpectedArg(String), + UnexpectedArg(Option), UnknownOption(char), + UnknownKeyFormat(&'static str), UnknownOptionFor(char, char), CannotCombine(char, char), BadTail(char, String), @@ -41,6 +43,8 @@ impl From<(String, ParseErrorKind)> for Error #[non_exhaustive] pub enum ConstructError { + NoGenerateKind, + ExpectedNotPresent(&'static str), AlreadyExists(String), Resolve(resolve::Error), } @@ -70,6 +74,7 @@ impl fmt::Display for ConstructError pub enum Error { Parse(String, ParseErrorKind), + ExpectedKind(Option), Unknown, AtomInternal, } diff --git a/src/args/mod.rs b/src/args/mod.rs index 58a3cd9..81e71df 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -58,8 +58,8 @@ EXAMPLE: #[instrument] /// Parse the program args into `Operation`. -pub async fn parse_args() -> Result +pub async fn parse_args() -> Result { - parse::parse(std::env::args().skip(1)) + parse::parse(std::env::args().skip(1)).await .with_suggestion(|| "Try passing `--help`") } diff --git a/src/args/parse.rs b/src/args/parse.rs deleted file mode 100644 index dc25d0c..0000000 --- a/src/args/parse.rs +++ /dev/null @@ -1,371 +0,0 @@ -//! Arg parsing -#![allow(unused_macros)] -use super::*; -use tokio::{ - sync::{ - mpsc, - }, -}; -use std::{ - iter, -}; -use eyre::eyre; -use config::op::{self, Password}; -use error::ParseErrorKind; - -#[derive(Debug, Hash, PartialEq, Eq)] -enum IsSigning -{ - No, - Yes, - Only, -} - -impl Default for IsSigning -{ - #[inline] - fn default() -> Self - { - Self::No - } -} - -#[derive(Debug, Hash, PartialEq, Eq)] -enum KeyKind -{ - Autodetect(IsSigning), - RsaPublic(IsSigning), - RsaPrivate(IsSigning), // is signing key? - Aes, -} - -#[derive(Debug, Hash, PartialEq, Eq)] -enum TranslationMode { - Autodetect, - Encrypt, - Decrypt, -} - -impl Default for TranslationMode -{ - #[inline] - fn default() -> Self - { - Self::Autodetect - } -} - - -#[derive(Debug, Hash, PartialEq, Eq)] -enum Atom -{ - Key(KeyKind, String, Password), - NonSpecific, - FileAuto(String), - FileSpecific(String, String, TranslationMode), - SetMode(config::op::Mode), -} - -impl Atom -{ - pub fn hash_into(&self, map: &mut smallmap::Map) -> bool - { - use std::hash::{Hash,Hasher,}; - let hasher = std::collections::hash_map::DefaultHasher::new(); - self.hash(&mut hasher); - let vl = hasher.finish(); - if map.contains_key(&vl) { - true - } else { - map.insert(hasher.finish(),()); - false - } - } -} - -#[instrument] -pub async fn parse + std::fmt::Debug>(args: I) -> Result -{ - let (mut tx, mut rx) = mpsc::channel(1); - - macro_rules! unwrap_handler { - ($handler:expr) => ($handler.await - .wrap_err_with(|| eyre!("Atom parser panicked") - .with_warning(|| "This usually indicates a bug in the program, not invalid input."))); - } - - let mut args = args.into_iter(); - let handler = if let Some(arg) = args.next() - { - match arg.to_lowercase().trim() { - "--generate" => todo!(), - "--help" => todo!(), - "--stat" => todo!(), - _ => { - let handler = tokio::spawn(async move { - let mut output = op::Normal::default(); - let mut had = smallmap::Map::new(); - while let Some(atom) = rx.recv().await { - debug!("[Normal] recv atom: {:?}", atom); - // Construct `Normal` with atoms - - if Atom::hash_into(&atom, &mut had) { - return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom))); - } - - match atom { - Atom::NonSpecific => output.in_place = true, - Atom::SetMode(mode) => output.mode = Some(mode), - Atom::FileAuto(file) => output.files.push((file, None, None)), - Atom::FileSpecific(finput, foutput, mode) => { - let mode = match mode { - TranslationMode::Autodetect => resolve::find_file_mode(&finput).await?, - TranslationMode::Encrypt => op::Mode::Encrypt, - TranslationMode::Decrypt => op::Mode::Decrypt, - }; - output.files.push((finput, Some(foutput), Some(mode))); - }, - Atom::Key(kind, string, password) => { - let (kind, sign) = match kind { - KeyKind::Autodetect(s) => (resolve::find_key_mode(&string).await?, s), - KeyKind::RsaPrivate(s) => (config::KeyKind::RsaPrivate, s), - KeyKind::RsaPublic(s) => (config::KeyKind::RsaPublic, s), - KeyKind::Aes => (config::KeyKind::Aes, Default::default()), - }; - if sign != IsSigning::No && kind == config::KeyKind::Aes { - warn!("Key was detected to not be RSA, option to sign is set and ignored."); - } else { - output.sign.push((string.clone(), password.clone())); - } - match kind { - config::KeyKind::Aes => output.aes.push((string, password)), - config::KeyKind::RsaPublic => output.rsa.push((string, password)), - config::KeyKind::RsaPrivate => output.rsa.push((string, password)) - } - }, - } - } - Ok(config::Operation::Normal(output)) - }.instrument(tracing::info_span!("handler"))); - match parse_normal(std::iter::once(arg) - .chain(args.map(Into::into)), &mut tx).await - { - // The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this. - Err(error::Error::AtomInternal) => Ok(()), - other => other, - } - .with_note(|| "In parsing of normal operation spec")?; - - - handler - }, - } - } else { - return Err(eyre!("No arguments specified")); - }; - - match unwrap_handler!(handler)? { - Err(error) => { - return Err(error) - .wrap_err_with(|| eyre!("Failed to construct operation from arguments")) - .with_note(|| "In constructing of operation from spec"); - }, - Ok(operation) => { - //TODO: Validate operation here? - Ok(operation) - }, - } -} - -async fn parse_normal>(args: I, atoms: &mut mpsc::Sender) -> Result<(), error::Error> -{ - let mut args = args.into_iter(); - let mut reading=true; - - let mut mode_set=false; - while let Some(arg) = args.next() - { - - macro_rules! take_one { - ($msg:literal $($tt:tt)*) => { - match args.next() { - Some(v) => v, - None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()), - } - } - } - if reading && arg.starts_with("-") { - let mut opt = arg.chars().skip(1); - - macro_rules! check_mode_set - { - () => { - if mode_set { - return Err((arg, error::ParseErrorKind::ModeSet).into()); - } else { - mode_set=true; - } - } - } - match opt.next() { - Some('a') => check_mode_set!(), - Some('e') => { - check_mode_set!(); - atoms.send(Atom::SetMode(config::op::Mode::Encrypt)).await?; - }, - Some('d') => { - check_mode_set!(); - atoms.send(Atom::SetMode(config::op::Mode::Decrypt)).await?; - }, - Some('i') => match opt.next() { - None => atoms.send(Atom::NonSpecific).await?, - Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()), - }, - Some('t') => { - let input = take_one!("t: Expected input file"); - let output = take_one!("t: Expected output file"); - let mut trans = TranslationMode::default(); - - let mut had = smallmap::Map::new(); - while let Some(opt) = opt.next() { - if (had.contains_key(&opt), had.insert(opt, ())).0 { - // arg has been repeated, not allowed - return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); - } - macro_rules! check_combine { - ($($chars:expr),+) => { - $(if had.contains_key(&$chars) { - return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); - })* - }; - } - - match opt { - 'e' => trans= TranslationMode::Encrypt, - 'd' => trans= TranslationMode::Decrypt, - _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()), - } - } - atoms.send(Atom::FileSpecific(input, output, trans)).await?; - }, - Some('k') => { - let key_file = take_one!("k: Expected a key file"); - enum KeySpec { - Autodetect, - RsaPrivate, - RsaPublic, - Aes, - } - let mut kind = KeySpec::Autodetect; - let mut password = Password::default(); - let mut signing = IsSigning::default(); - - let mut had = smallmap::Map::new(); - while let Some(opt) = opt.next() { - if (had.contains_key(&opt), had.insert(opt, ())).0 { - // arg has been repeated, not allowed - return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); - } - macro_rules! check_combine { - ($($chars:expr),+) => { - $(if had.contains_key(&$chars) { - return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); - })* - }; - } - - match opt { - 'R' => { - check_combine!['r','a']; - kind = KeySpec::RsaPrivate; - }, - 'r' => { - check_combine!['R','a']; - kind = KeySpec::RsaPublic; - }, - 'a' => { - check_combine!['r','R']; - kind = KeySpec::Aes; - }, - 'P' => { - check_combine!['p']; - password= Password::Yes; - }, - 'p' => { - check_combine!['P']; - password = Password::Specific(take_one!("P: Expected a password")); - }, - 'S' => { - check_combine!['s', 'a']; - signing = IsSigning::Only; - }, - 's' => { - check_combine!['S', 'a']; - signing = IsSigning::Yes; - }, - _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()), - } - } - - let kind = match kind { - KeySpec::Autodetect => KeyKind::Autodetect(signing), - KeySpec::RsaPublic => KeyKind::RsaPublic(signing), - KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing), - KeySpec::Aes => KeyKind::Aes, - }; - atoms.send(Atom::Key(kind, key_file, password)).await?; - }, - Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()), - None => reading=false, - } - } else { - reading=false; - atoms.send(Atom::FileAuto(arg)).await?; - } - } - todo!() - -} - - - -/* -#[instrument] -pub fn parse, I: IntoIterator + std::fmt::Debug>(args: I) -> Result -{ -let mut args = args.into_iter(); -if let Some(arg) = args.next() -{ -let arg =arg.into(); -match arg.to_lowercase().trim() { -"--generate" => Ok(Spec::Concrete(config::Operation::GenerateKey(parse_keygen(args.map(Into::into)) -.with_note(|| "Mode was `--generate`")?))), -"--help" => Ok(Spec::Concrete(config::Operation::Help)), -"--stat" => Ok(Spec::Concrete(config::Operation::KeyInfo(parse_stat(args.map(Into::into)) -.with_note(|| "Mode was `--stat")?))), -_ => Ok(parse_normal(std::iter::once(arg) -.chain(args.map(Into::into))) -.with_note(|| "Mode was crypt")?.into()), - } - } else { - Err(eyre!("No arguments specified")) - } -} - -fn parse_normal>(args: I) -> Result -{ - todo!() -} - - -fn parse_keygen>(args: I) -> Result -{ - todo!() -} - - -fn parse_stat>(args: I) -> Result, eyre::Report> -{ - todo!() -} -*/ diff --git a/src/args/parse/generate.rs b/src/args/parse/generate.rs new file mode 100644 index 0000000..a8139ce --- /dev/null +++ b/src/args/parse/generate.rs @@ -0,0 +1,166 @@ +//! Parser for generate mode +use super::*; +use tokio::sync::mpsc::Receiver; + +#[instrument] +pub(super) async fn handle(mut rx: Receiver) -> Result +{ + let mut had = smallmap::Map::new(); + + #[derive(Default)] + struct Output + { + kind: Option, + input: Option, + password: Password, + input_password: Password, + format: KeyFormat, + output: Option, + output_public: Option, + } + + #[cold] + #[inline(never)] + fn _invalidate(msg: D) -> ! + { + panic!("{}. This should've been handled by `parse()`", msg); + } + + macro_rules! invalidate { + + ($msg:literal) => { + _invalidate($msg); + }; + ($msg:literal $($tt:tt)*) => { + _invalidate(format!($msg $($tt)*)); + } + } + let mut stage = Output{ + kind: Some(match rx.recv().await.unwrap() { + Atom::Generate(GenerateAtom::Type(ty)) => ty, + _ => invalidate!("First atom is not type."), + }), + ..Output::default() + }; + + while let Some(Atom::Generate(atom)) = rx.recv().await + { + if GenerateAtom::hash_into(&atom, &mut had) { + return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom))); + } + match atom { + GenerateAtom::Type(_) => invalidate!("Type cannot be specified twice."), + GenerateAtom::Input(input) => stage.input = Some(input), + GenerateAtom::Password(passwd) => stage.password = passwd, + GenerateAtom::Format(format) => stage.format =format, + GenerateAtom::Output(output) => stage.output = Some(output), + GenerateAtom::PublicOutput(output) => stage.output_public = Some(output), + GenerateAtom::InputPassword(passwd) => stage.input_password = passwd, + } + } + + let input_pw = stage.input_password; + let output = if let Some(GenerateKeyKind::Rsa) = stage.kind { + //RSA + config::op::GenerateKey::Rsa(config::op::Rsa { + input: stage.input.map(|inp| (inp, input_pw)), + output_priv: stage.output.ok_or_else(|| error::ConstructError::ExpectedNotPresent("output"))?, + output_pub: stage.output_public, + password: stage.password, + description: Default::default(), /* TODO */ + }) + } else { + //AES + config::op::GenerateKey::Aes(config::op::Aes { + input: stage.input.map(|inp| (inp, input_pw)), + output: stage.output.ok_or_else(|| error::ConstructError::ExpectedNotPresent("output"))?, + password: stage.password, + description: Default::default(), /* TODO */ + }) + }; + Ok(config::Operation::GenerateKey(output)) +} + +fn split<'a>(input: &'a str) -> (&'a str, Option<&'a str>) +{ + if let Some(index) = input.find('=') { + (&input[..index], Some(&input[(index+1)..])) + } else { + (input, None) + } +} + + +pub(super) async fn parse>(args: I, atoms: &mut mpsc::Sender) -> Result<(), error::Error> +{ + let mut args = args.into_iter(); + let mut reading = true; + let mut sent_output=0usize; + + macro_rules! send { + ($atom:expr) => { + atoms.send(Atom::Generate($atom)) + } + } + + let (mode, _) = { + let arg = args.next(); + match arg.as_ref().map(|x| x.trim().to_uppercase()).as_deref() { + Some("RSA") => (GenerateKeyKind::Rsa, send!(GenerateAtom::Type(GenerateKeyKind::Rsa)).await?), + Some("AES") => (GenerateKeyKind::Aes, send!(GenerateAtom::Type(GenerateKeyKind::Aes)).await?), + _ => return Err(error::Error::ExpectedKind(arg)), + } + }; + + + while let Some(arg) = args.next() + { + macro_rules! take_one { + ($msg:literal $($tt:tt)*) => { + match args.next() { + Some(v) => v, + None => return Err((arg, error::ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()), + } + } + } + + if reading && arg.starts_with("--") { + let (key, value) = split(&arg); + match &key.to_lowercase().trim()[2..] { + "input" => send!(GenerateAtom::Input(take_one!("--input: Expected filename"))).await?, + "password" if value.is_some() => { + let value = value.unwrap(); + send!(GenerateAtom::Password(Password::Specific(value.to_owned()))).await?; + }, + "password" => send!(GenerateAtom::Password(Password::Yes)).await?, + "format" if mode.is_rsa() => + send!(GenerateAtom::Format(match take_one!("--format: Expected output format").to_uppercase().trim() { + "PEM" => KeyFormat::PEM, + "BIN" => KeyFormat::Bin, + "TEXT" => KeyFormat::Text, + __ => return Err((arg, error::ParseErrorKind::UnknownKeyFormat("rsa")).into()), + })).await?, + "format" => + send!(GenerateAtom::Format(match take_one!("--format: Expected output format").to_uppercase().trim() { + "BIN" => KeyFormat::Bin, + "TEXT" => KeyFormat::Text, + __ => return Err((arg, error::ParseErrorKind::UnknownKeyFormat("aes")).into()), + })).await?, + _ => return Err((arg, error::ParseErrorKind::UnexpectedArg(None)).into()), + } + } else { + reading=false; + match sent_output + { + 0 => send!(GenerateAtom::Output(arg)).await?, + 1 if mode.is_rsa() => send!(GenerateAtom::PublicOutput(arg)).await?, + _ => return Err((arg, error::ParseErrorKind::UnexpectedArg(None)).into()), + } + sent_output +=1; + } + + } + + todo!() +} + diff --git a/src/args/parse/mod.rs b/src/args/parse/mod.rs new file mode 100644 index 0000000..4ad0fcb --- /dev/null +++ b/src/args/parse/mod.rs @@ -0,0 +1,257 @@ +//! Arg parsing +#![allow(unused_macros)] +use super::*; +use tokio::{ + sync::{ + mpsc, + }, +}; +use std::{ + iter, +}; +use eyre::eyre; +use config::op::{self, Password}; +use error::ParseErrorKind; + +mod normal; +mod generate; + +#[derive(Debug, Hash, PartialEq, Eq)] +enum IsSigning +{ + No, + Yes, + Only, +} + +impl Default for IsSigning +{ + #[inline] + fn default() -> Self + { + Self::No + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum KeyKind +{ + Autodetect(IsSigning), + RsaPublic(IsSigning), + RsaPrivate(IsSigning), // is signing key? + Aes, +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum TranslationMode { + Autodetect, + Encrypt, + Decrypt, +} + +impl Default for TranslationMode +{ + #[inline] + fn default() -> Self + { + Self::Autodetect + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum KeyFormat +{ + Bin, + Text, + PEM, +} + +impl Default for KeyFormat +{ + #[inline] + fn default() -> Self + { + Self::Bin + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum GenerateKeyKind +{ + Rsa, + Aes, +} + +impl GenerateKeyKind +{ + #[inline(always)] + pub fn is_rsa(&self) -> bool + { + if let Self::Rsa = self { + true + } else { + false + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum GenerateAtom +{ + Type(GenerateKeyKind), + Input(String), + InputPassword(Password), + Password(Password), + Format(KeyFormat), + Output(String), + PublicOutput(String), +} + +impl GenerateAtom +{ + pub fn hash_into(&self, map: &mut smallmap::Map) -> bool + { + use std::hash::{Hash,Hasher,}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + self.hash(&mut hasher); + let vl = hasher.finish(); + if map.contains_key(&vl) { + true + } else { + map.insert(hasher.finish(),()); + false + } + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +enum Atom +{ + Key(KeyKind, String, Password), + NonSpecific, + FileAuto(String), + FileSpecific(String, String, TranslationMode), + SetMode(config::op::Mode), + + Generate(GenerateAtom), +} + +impl Atom +{ + pub fn hash_into(&self, map: &mut smallmap::Map) -> bool + { + use std::hash::{Hash,Hasher,}; + let mut hasher = std::collections::hash_map::DefaultHasher::new(); + self.hash(&mut hasher); + let vl = hasher.finish(); + if map.contains_key(&vl) { + true + } else { + map.insert(hasher.finish(),()); + false + } + } +} + +#[instrument] +pub async fn parse + std::fmt::Debug>(args: I) -> Result +{ + let (mut tx, rx) = mpsc::channel(1); + + macro_rules! unwrap_handler { + ($handler:expr) => ($handler.await + .wrap_err_with(|| eyre!("Atom parser panicked") + .with_warning(|| "This usually indicates a bug in the program, not invalid input."))); + } + + let mut args = args.into_iter(); + let handler = if let Some(arg) = args.next() + { + match arg.to_lowercase().trim() { + "--generate" => { + let handler = tokio::spawn(generate::handle(rx)); + match generate::parse(std::iter::once(arg) + .chain(args.map(Into::into)), &mut tx).await + { + // The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this. + Err(error::Error::AtomInternal) => Ok(()), + other => other, + } + .with_note(|| "In parsing of generate operation spec")?; + + handler + }, + "--help" => return Ok(config::Operation::Help), + "--stat" => todo!(), + _ => { + let handler = tokio::spawn(normal::handle(rx)); + match normal::parse(std::iter::once(arg) + .chain(args.map(Into::into)), &mut tx).await + { + // The `handler` has dropped `rx` and returned an error, let unwrap_handler down there handle this. + Err(error::Error::AtomInternal) => Ok(()), + other => other, + } + .with_note(|| "In parsing of normal operation spec")?; + + handler + }, + } + } else { + return Err(eyre!("No arguments specified")); + }; + + match unwrap_handler!(handler)? { + Err(error) => { + return Err(error) + .wrap_err_with(|| eyre!("Failed to construct operation from arguments")) + .with_note(|| "In constructing of operation from spec"); + }, + Ok(operation) => { + //TODO: Validate operation here? + Ok(operation) + }, + } +} + + +/* +#[instrument] +pub fn parse, I: IntoIterator + std::fmt::Debug>(args: I) -> Result +{ +let mut args = args.into_iter(); +if let Some(arg) = args.next() +{ +let arg =arg.into(); +match arg.to_lowercase().trim() { +"--generate" => Ok(Spec::Concrete(config::Operation::GenerateKey(parse_keygen(args.map(Into::into)) +.with_note(|| "Mode was `--generate`")?))), +"--help" => Ok(Spec::Concrete(config::Operation::Help)), +"--stat" => Ok(Spec::Concrete(config::Operation::KeyInfo(parse_stat(args.map(Into::into)) +.with_note(|| "Mode was `--stat")?))), +_ => Ok(parse_normal(std::iter::once(arg) +.chain(args.map(Into::into))) +.with_note(|| "Mode was crypt")?.into()), + } + } else { + Err(eyre!("No arguments specified")) + } +} + +fn parse_normal>(args: I) -> Result +{ + todo!() +} + + +fn parse_keygen>(args: I) -> Result +{ + todo!() +} + + +fn parse_stat>(args: I) -> Result, eyre::Report> +{ + todo!() +} +*/ diff --git a/src/args/parse/normal.rs b/src/args/parse/normal.rs new file mode 100644 index 0000000..d1c2b23 --- /dev/null +++ b/src/args/parse/normal.rs @@ -0,0 +1,203 @@ +//! Parser for normal mode +use super::*; +use tokio::sync::mpsc::Receiver; + +#[instrument] +pub(super) async fn handle(mut rx: Receiver) -> Result +{ + let mut output = op::Normal::default(); + let mut had = smallmap::Map::new(); + while let Some(atom) = rx.recv().await { + debug!("[Normal] recv atom: {:?}", atom); + // Construct `Normal` with atoms + + if Atom::hash_into(&atom, &mut had) { + return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom))); + } + + match atom { + Atom::NonSpecific => output.in_place = true, + Atom::SetMode(mode) => output.mode = Some(mode), + Atom::FileAuto(file) => output.files.push((file, None, None)), + Atom::FileSpecific(finput, foutput, mode) => { + let mode = match mode { + TranslationMode::Autodetect => resolve::find_file_mode(&finput).await?, + TranslationMode::Encrypt => op::Mode::Encrypt, + TranslationMode::Decrypt => op::Mode::Decrypt, + }; + output.files.push((finput, Some(foutput), Some(mode))); + }, + Atom::Key(kind, string, password) => { + let (kind, sign) = match kind { + KeyKind::Autodetect(s) => (resolve::find_key_mode(&string).await?, s), + KeyKind::RsaPrivate(s) => (config::KeyKind::RsaPrivate, s), + KeyKind::RsaPublic(s) => (config::KeyKind::RsaPublic, s), + KeyKind::Aes => (config::KeyKind::Aes, Default::default()), + }; + if sign != IsSigning::No && kind == config::KeyKind::Aes { + warn!("Key was detected to not be RSA, option to sign is set and ignored."); + } else { + output.sign.push((string.clone(), password.clone())); + } + match kind { + config::KeyKind::Aes => output.aes.push((string, password)), + config::KeyKind::RsaPublic => output.rsa.push((string, password)), + config::KeyKind::RsaPrivate => output.rsa.push((string, password)) + } + }, + Atom::Generate(_) => unreachable!(), + } + } + Ok(config::Operation::Normal(output)) +} + +pub(super) async fn parse>(args: I, atoms: &mut mpsc::Sender) -> Result<(), error::Error> +{ + let mut args = args.into_iter(); + let mut reading=true; + + let mut mode_set=false; + while let Some(arg) = args.next() + { + + macro_rules! take_one { + ($msg:literal $($tt:tt)*) => { + match args.next() { + Some(v) => v, + None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()), + } + } + } + if reading && arg.starts_with("-") { + let mut opt = arg.chars().skip(1); + + macro_rules! check_mode_set + { + () => { + if mode_set { + return Err((arg, error::ParseErrorKind::ModeSet).into()); + } else { + mode_set=true; + } + } + } + match opt.next() { + Some('a') => check_mode_set!(), + Some('e') => { + check_mode_set!(); + atoms.send(Atom::SetMode(config::op::Mode::Encrypt)).await?; + }, + Some('d') => { + check_mode_set!(); + atoms.send(Atom::SetMode(config::op::Mode::Decrypt)).await?; + }, + Some('i') => match opt.next() { + None => atoms.send(Atom::NonSpecific).await?, + Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()), + }, + Some('t') => { + let input = take_one!("t: Expected input file"); + let output = take_one!("t: Expected output file"); + let mut trans = TranslationMode::default(); + + let mut had = smallmap::Map::new(); + while let Some(opt) = opt.next() { + if (had.contains_key(&opt), had.insert(opt, ())).0 { + // arg has been repeated, not allowed + return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); + } + macro_rules! check_combine { + ($($chars:expr),+) => { + $(if had.contains_key(&$chars) { + return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); + })* + }; + } + + match opt { + 'e' => trans= TranslationMode::Encrypt, + 'd' => trans= TranslationMode::Decrypt, + _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()), + } + } + atoms.send(Atom::FileSpecific(input, output, trans)).await?; + }, + Some('k') => { + let key_file = take_one!("k: Expected a key file"); + enum KeySpec { + Autodetect, + RsaPrivate, + RsaPublic, + Aes, + } + let mut kind = KeySpec::Autodetect; + let mut password = Password::default(); + let mut signing = IsSigning::default(); + + let mut had = smallmap::Map::new(); + while let Some(opt) = opt.next() { + if (had.contains_key(&opt), had.insert(opt, ())).0 { + // arg has been repeated, not allowed + return Err((arg, error::ParseErrorKind::NotUnique(opt)).into()); + } + macro_rules! check_combine { + ($($chars:expr),+) => { + $(if had.contains_key(&$chars) { + return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars)))); + })* + }; + } + + match opt { + 'R' => { + check_combine!['r','a']; + kind = KeySpec::RsaPrivate; + }, + 'r' => { + check_combine!['R','a']; + kind = KeySpec::RsaPublic; + }, + 'a' => { + check_combine!['r','R']; + kind = KeySpec::Aes; + }, + 'P' => { + check_combine!['p']; + password= Password::Yes; + }, + 'p' => { + check_combine!['P']; + password = Password::Specific(take_one!("P: Expected a password")); + }, + 'S' => { + check_combine!['s', 'a']; + signing = IsSigning::Only; + }, + 's' => { + check_combine!['S', 'a']; + signing = IsSigning::Yes; + }, + _ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()), + } + } + + let kind = match kind { + KeySpec::Autodetect => KeyKind::Autodetect(signing), + KeySpec::RsaPublic => KeyKind::RsaPublic(signing), + KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing), + KeySpec::Aes => KeyKind::Aes, + }; + atoms.send(Atom::Key(kind, key_file, password)).await?; + }, + Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()), + None => reading=false, + } + } else { + reading=false; + atoms.send(Atom::FileAuto(arg)).await?; + } + } + + Ok(()) +} + diff --git a/src/config.rs b/src/config.rs index 2e01a9c..0757c10 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,29 +50,29 @@ pub mod op { pub email: String, pub description: String, - other: String, + pub other: String, } #[derive(Debug, Clone,PartialEq, Eq)] pub struct Rsa { - input: Option<(String, Password)>, + pub input: Option<(String, Password)>, - output_priv: String, - output_pub: Option, - password: Password, + pub output_priv: String, + pub output_pub: Option, + pub password: Password, - description: KeyDescription, + pub description: KeyDescription, } #[derive(Debug,Clone, PartialEq, Eq)] pub struct Aes { - input: Option<(String, Password)>, + pub input: Option<(String, Password)>, - output: String, - password: Password, + pub output: String, + pub password: Password, - description: KeyDescription, + pub description: KeyDescription, } } #[derive(Debug, Clone,PartialEq, Eq)]