use std::{io::Read, path::PathBuf};
use clap::Parser;
use log::info;
use crate::{
store::{EncryptedStore, EncryptedStoreError, StoreError},
Config, LeafCommand,
};
#[derive(Debug, Parser)]
pub struct ListValues {}
impl LeafCommand for ListValues {
type Error = ValueError;
fn run(&self, config: &Config) -> Result<(), ValueError> {
let store = EncryptedStore::new(config.sop(), config.store()).read_store()?;
info!("loaded store OK");
let mut names: Vec<&str> = store.iter().map(|(k, _)| k).collect();
names.sort();
for name in names {
println!("{name}");
}
Ok(())
}
}
#[derive(Debug, Parser)]
pub struct AddValue {
name: String,
#[clap(long)]
file: Option<PathBuf>,
#[clap(long, default_value = "false", action = clap::ArgAction::SetTrue)]
stdin: Option<bool>,
}
impl AddValue {
fn value(&self) -> Result<Vec<u8>, ValueError> {
match (&&self.file, self.stdin) {
(Some(_), Some(true)) => Err(ValueError::ThereCanBeOnlyOne),
(None, None) => Err(ValueError::ThereMustBeOne),
(None, Some(false)) => Err(ValueError::ThereMustBeOne),
(Some(filename), _) => {
let value = std::fs::read(filename)
.map_err(|err| ValueError::ReadFile(filename.into(), err))?;
Ok(value)
}
(_, Some(true)) => {
let mut stdin = std::io::stdin();
let mut value = vec![];
stdin
.read_to_end(&mut value)
.map_err(ValueError::ReadStdin)?;
Ok(value)
}
}
}
}
impl LeafCommand for AddValue {
type Error = ValueError;
fn run(&self, config: &Config) -> Result<(), ValueError> {
info!("AddValue self={self:#?}");
let encrypted = EncryptedStore::new(config.sop(), config.store());
let mut store = encrypted.read_store()?;
store.insert(&self.name, &self.value()?);
encrypted.write_store(&store)?;
Ok(())
}
}
#[derive(Debug, Parser)]
pub struct ShowValue {
name: String,
}
impl LeafCommand for ShowValue {
type Error = ValueError;
fn run(&self, config: &Config) -> Result<(), ValueError> {
let encrypted = EncryptedStore::new(config.sop(), config.store());
let store = encrypted.read_store()?;
info!("get value for {}", self.name);
if let Some(value) = store.get(&self.name) {
println!("{}", String::from_utf8_lossy(value));
} else {
return Err(ValueError::NoSuchValue(self.name.clone()));
}
Ok(())
}
}
#[derive(Debug, Parser)]
pub struct RemoveValue {
name: String,
}
impl LeafCommand for RemoveValue {
type Error = ValueError;
fn run(&self, config: &Config) -> Result<(), ValueError> {
info!("remove value for {}", self.name);
let encrypted = EncryptedStore::new(config.sop(), config.store());
let mut store = encrypted.read_store()?;
store.remove(&self.name);
encrypted.write_store(&store)?;
Ok(())
}
}
#[derive(Debug, Parser)]
pub struct RenameValue {
old: String,
new: String,
}
impl LeafCommand for RenameValue {
type Error = ValueError;
fn run(&self, config: &Config) -> Result<(), ValueError> {
info!("rename {} to {}", self.old, self.new);
let encrypted = EncryptedStore::new(config.sop(), config.store());
let mut store = encrypted.read_store()?;
info!("rename in memory");
store.rename(&self.old, &self.new)?;
info!("rename in memory OK");
encrypted.write_store(&store)?;
Ok(())
}
}
#[derive(Debug, thiserror::Error)]
pub enum ValueError {
#[error("there is not value named {0}")]
NoSuchValue(String),
#[error(transparent)]
Encrypted(#[from] EncryptedStoreError),
#[error(transparent)]
Store(#[from] StoreError),
#[error("conflict: more than one way used to specify value to be added")]
ThereCanBeOnlyOne,
#[error("no value to add specified")]
ThereMustBeOne,
#[error("failed to read value from file {0}")]
ReadFile(PathBuf, #[source] std::io::Error),
#[error("failed to read value from stdin")]
ReadStdin(#[source] std::io::Error),
}