//! Arg parsing #![allow(unused_macros)] use super::*; use tokio::{ sync::{ mpsc, }, }; use std::{ iter, }; use eyre::eyre; use config::op::Password; use error::ParseErrorKind; #[derive(Debug)] enum IsSigning { No, Yes, Only, } impl Default for IsSigning { #[inline] fn default() -> Self { Self::No } } #[derive(Debug)] enum KeyKind { Autodetect, RsaPublic, RsaPrivate(IsSigning), // is signing key? Aes, } #[derive(Debug)] enum TranslationMode { Autodetect, Encrypt, Decrypt, } impl Default for TranslationMode { #[inline] fn default() -> Self { Self::Autodetect } } #[derive(Debug)] enum Atom { Key(KeyKind, String, Password), NonSpecific, FileAuto(String), FileSpecific(String, String, TranslationMode), //TODO: } #[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 { //TODO: What container type do we make for this? while let Some(atom) = rx.recv().await { debug!("[Normal] recv atom: {:?}", atom); // Construct `Normal` with atoms } todo!() }); 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(result) => { }, } todo!() } async fn parse_normal>(args: I, atoms: &mut mpsc::Sender) -> Result<(), error::Error> { let mut args = args.into_iter(); let mut reading=true; 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); match opt.next() { 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, KeySpec::RsaPublic => KeyKind::RsaPublic, 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!() } */