You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
9.7 KiB

//! 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<u64, ()>) -> 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<I: IntoIterator<Item=String> + std::fmt::Debug>(args: I) -> Result<config::Operation, eyre::Report>
{
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<I: IntoIterator<Item=String>>(args: I, atoms: &mut mpsc::Sender<Atom>) -> 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<T: Into<String>, I: IntoIterator<Item = T> + std::fmt::Debug>(args: I) -> Result<Spec, eyre::Report>
{
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<I: IntoIterator<Item = String>>(args: I) -> Result<OperationSpec, eyre::Report>
{
todo!()
}
fn parse_keygen<I: IntoIterator<Item = String>>(args: I) -> Result<config::op::GenerateKey, eyre::Report>
{
todo!()
}
fn parse_stat<I: IntoIterator<Item = String>>(args: I) -> Result<Vec<(String, config::op::Password)>, eyre::Report>
{
todo!()
}
*/