use crate::prelude_internal::*;
use std::collections::{HashMap, HashSet};
pub trait ToolState: AsMut<Self> {
type Secret: Derivable;
fn root_secret(&self) -> Result<Self::Secret>;
fn current_secret(&self) -> Result<Self::Secret>;
fn import(&mut self, secret: &Secret) -> Result;
fn export(&self) -> Result<Secret>;
fn generate(&mut self) -> Result;
fn load(&mut self, path: &Path) -> Result;
fn save(&self, path: &Path) -> Result;
fn get_keypath(&self) -> Result<String>;
fn update_keypath(&mut self, relpath: &str) -> Result;
fn key_map_mut(&mut self) -> &mut KeyMap;
}
#[derive(Debug, Default)]
pub struct KeyMap {
pub(crate) children: HashMap<String, KeyMap>,
pub(crate) primitives: HashSet<String>,
}
impl KeyMap {
pub fn get_primitives(&self) -> impl Iterator<Item = &str> {
self.primitives.iter().map(|x| x.as_str())
}
pub fn add_primitive<T: Into<String>>(&mut self, primitive: T) {
self.primitives.insert(primitive.into());
}
pub fn get_children(&self) -> impl Iterator<Item = &str> {
self.children.keys().map(|x| x.as_str())
}
pub fn get_child<T: AsRef<str>>(&self, child: T) -> Option<&KeyMap> {
self.children.get(child.as_ref())
}
pub fn get_child_mut<T: AsRef<str>>(&mut self, child: T) -> Option<&mut KeyMap> {
self.children.get_mut(child.as_ref())
}
pub fn get_key_map_from_iter<'a, T: IntoIterator<Item = &'a str>>(
&self,
iter: T,
) -> Option<&KeyMap> {
let mut iter = iter.into_iter();
if let Some(label) = iter.next() {
if let Some(keymap) = self.children.get(label) {
keymap.get_key_map_from_iter(iter)
} else {
None
}
} else {
Some(self)
}
}
pub fn get_key_map<T: AsRef<str>>(&self, keypath: T) -> Option<&KeyMap> {
self.get_key_map_from_iter(keypath.as_ref().split('/').filter(|x| !x.is_empty()))
}
pub fn update_from_iter<'a, T: IntoIterator<Item = &'a str>>(
&mut self,
iter: T,
) -> Option<&mut KeyMap> {
let mut iter = iter.into_iter();
if let Some(label) = iter.next() {
let keymap = self
.children
.entry(label.to_string())
.or_insert(KeyMap::default());
keymap.update_from_iter(iter)
} else {
Some(self)
}
}
pub fn update<T: AsRef<str>>(&mut self, keypath: T) -> Option<&mut KeyMap> {
self.update_from_iter(keypath.as_ref().split('/').filter(|x| !x.is_empty()))
}
}
#[derive(Debug)]
pub struct StandardToolState {
keypath: String,
secret: Option<Secret>,
key_map: KeyMap,
}
impl Default for StandardToolState {
fn default() -> Self {
StandardToolState {
keypath: "/".to_string(),
secret: None,
key_map: Default::default(),
}
}
}
impl StandardToolState {
pub fn reset(&mut self) {
*self = Self::default()
}
}
impl AsRef<StandardToolState> for StandardToolState {
fn as_ref(&self) -> &StandardToolState {
self
}
}
impl AsMut<StandardToolState> for StandardToolState {
fn as_mut(&mut self) -> &mut StandardToolState {
self
}
}
impl ToolState for StandardToolState {
type Secret = Secret;
fn root_secret(&self) -> Result<Self::Secret> {
self.secret.clone().ok_or(format_err!("No secret set"))
}
fn current_secret(&self) -> Result<Self::Secret> {
if let Some(secret) = self.secret.as_ref() {
secret.subsecret_from_path(&self.keypath)
} else {
bail!("No secret set.");
}
}
fn import(&mut self, secret: &Secret) -> Result<()> {
self.reset();
self.secret = Some(secret.clone());
Ok(())
}
fn export(&self) -> Result<Secret> {
self.current_secret()
}
fn generate(&mut self) -> Result<()> {
self.reset();
self.secret = Some(Secret::generate());
Ok(())
}
fn load(&mut self, path: &Path) -> Result<()> {
let data = std::fs::read(path)?;
self.reset();
self.secret = Some(Secret::try_from_bytes_or_hex(data)?);
Ok(())
}
fn save(&self, path: &Path) -> Result<()> {
if let Some(secret) = &self.secret {
std::fs::write(path, secret.as_bytes())?;
} else {
bail!("No secret set.");
}
Ok(())
}
fn get_keypath(&self) -> Result<String> {
Ok(self.keypath.clone())
}
fn update_keypath(&mut self, relpath: &str) -> Result<()> {
if relpath.starts_with('/') {
self.keypath = relpath.to_string();
return Ok(());
}
let mut path: Vec<String> = self
.keypath
.split('/')
.filter_map(|w| {
if w.is_empty() {
None
} else {
Some(w.to_string())
}
})
.collect();
for label in relpath.split('/').filter(|w| !w.is_empty()) {
match label {
"." => {
continue;
}
".." => {
path.pop();
}
label => {
path.push(label.to_string());
}
};
}
path.insert(0, String::new());
self.keypath = path.join("/");
if self.keypath.is_empty() {
self.keypath.insert(0, '/');
}
Ok(())
}
fn key_map_mut(&mut self) -> &mut KeyMap {
&mut self.key_map
}
}