use std::io::{self, Read};
use anyhow::{anyhow, Result};
use inquire::{Password as Pass, Text};
use super::record::{EnvironmentFields, LoginFields, RecordBody, UnstructuredFields};
use crate::kbs2::config::RuntimeConfig;
pub static TERSE_IFS: &str = "\x01";
pub trait Input {
const FIELD_COUNT: usize;
fn from_prompt(config: &RuntimeConfig) -> Result<RecordBody>;
fn from_terse(config: &RuntimeConfig) -> Result<RecordBody>;
fn take_terse_fields() -> Result<Vec<String>> {
let mut input = String::new();
io::stdin().read_to_string(&mut input)?;
if input.ends_with('\n') {
input.pop();
}
let fields = input
.splitn(Self::FIELD_COUNT, TERSE_IFS)
.map(Into::into)
.collect::<Vec<String>>();
if fields.len() != Self::FIELD_COUNT {
return Err(anyhow!(
"field count mismatch: expected {}, got {}",
Self::FIELD_COUNT,
fields.len()
));
}
Ok(fields)
}
fn input(config: &RuntimeConfig) -> Result<RecordBody> {
if config.terse() {
Self::from_terse(config)
} else {
Self::from_prompt(config)
}
}
}
impl Input for LoginFields {
const FIELD_COUNT: usize = 2;
fn from_prompt(config: &RuntimeConfig) -> Result<RecordBody> {
let username = if let Some(default_username) = &config.config.commands.new.default_username
{
Text::new("Username?")
.with_default(default_username)
.prompt()?
} else {
Text::new("Username?").prompt()?
};
let mut password = Pass::new("Password?")
.with_help_message("Press [enter] to auto-generate")
.prompt()?;
if password.is_empty() {
password = config.generator()?.secret()?;
}
Ok(RecordBody::Login(LoginFields { username, password }))
}
fn from_terse(config: &RuntimeConfig) -> Result<RecordBody> {
let (mut password, username) = {
let mut fields = Self::take_terse_fields()?;
#[allow(clippy::unwrap_used)]
(fields.pop().unwrap(), fields.pop().unwrap())
};
if password.is_empty() {
password = config.generator()?.secret()?;
}
Ok(RecordBody::Login(LoginFields { username, password }))
}
}
impl Input for EnvironmentFields {
const FIELD_COUNT: usize = 2;
fn from_prompt(config: &RuntimeConfig) -> Result<RecordBody> {
let variable = Text::new("Variable?").prompt()?;
let mut value = Pass::new("Value?")
.with_help_message("Press [enter] to auto-generate")
.prompt()?;
if value.is_empty() {
value = config.generator()?.secret()?;
}
Ok(RecordBody::Environment(EnvironmentFields {
variable,
value,
}))
}
fn from_terse(config: &RuntimeConfig) -> Result<RecordBody> {
let (mut value, variable) = {
let mut fields = Self::take_terse_fields()?;
#[allow(clippy::unwrap_used)]
(fields.pop().unwrap(), fields.pop().unwrap())
};
if value.is_empty() {
value = config.generator()?.secret()?;
}
Ok(RecordBody::Environment(EnvironmentFields {
variable,
value,
}))
}
}
impl Input for UnstructuredFields {
const FIELD_COUNT: usize = 1;
fn from_prompt(_config: &RuntimeConfig) -> Result<RecordBody> {
let contents = Text::new("Contents?").prompt()?;
Ok(RecordBody::Unstructured(UnstructuredFields { contents }))
}
fn from_terse(_config: &RuntimeConfig) -> Result<RecordBody> {
#[allow(clippy::unwrap_used)]
let contents = Self::take_terse_fields()?.pop().unwrap();
Ok(RecordBody::Unstructured(UnstructuredFields { contents }))
}
}