use std::ffi; use std::fmt; use std::process; use std::str; #[derive(Debug, structopt::StructOpt)] pub struct Args { #[structopt(long, hidden(true))] pub nitrocli: String, #[structopt(long, hidden(true))] pub verbosity: Option, } #[derive(Debug)] pub struct Nitrocli { cmd: process::Command, } #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum OtpAlgorithm { Hotp, Totp, } impl Args { pub fn nitrocli(&self) -> Nitrocli { // TODO: set verbosity args Nitrocli::new(&self.nitrocli) } } impl Nitrocli { pub fn new(nitrocli: &str) -> Nitrocli { Nitrocli { cmd: process::Command::new(nitrocli), } } pub fn arg(&mut self, arg: impl AsRef) -> &mut Nitrocli { self.cmd.arg(arg); self } pub fn args(&mut self, args: I) -> &mut Nitrocli where I: IntoIterator, S: AsRef, { self.cmd.args(args); self } pub fn text(&mut self) -> anyhow::Result { let output = self.cmd.output()?; if output.status.success() { String::from_utf8(output.stdout).map_err(From::from) } else { Err(anyhow::anyhow!( "nitrocli call failed: {}", String::from_utf8_lossy(&output.stderr) )) } } } impl fmt::Display for OtpAlgorithm { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}", match self { OtpAlgorithm::Hotp => "hotp", OtpAlgorithm::Totp => "totp", } ) } } impl str::FromStr for OtpAlgorithm { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "hotp" => Ok(OtpAlgorithm::Hotp), "totp" => Ok(OtpAlgorithm::Totp), _ => Err(anyhow::anyhow!("Unexpected OTP algorithm: {}", s)), } } } impl<'de> serde::Deserialize<'de> for OtpAlgorithm { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::de::Error as _; str::FromStr::from_str(&String::deserialize(deserializer)?).map_err(D::Error::custom) } } impl serde::Serialize for OtpAlgorithm { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { self.to_string().serialize(serializer) } }