use std::collections::hash_map::{DefaultHasher, HashMap};
use std::fs;
use std::hash::{Hash, Hasher};
use std::io::{Read, Write};
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum PersistKind {
AccountPrivateKey,
PrivateKey,
Certificate,
}
impl PersistKind {
fn name(self) -> &'static str {
match self {
PersistKind::Certificate => "crt",
PersistKind::PrivateKey => "key",
PersistKind::AccountPrivateKey => "key",
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct PersistKey<'a> {
pub realm: u64,
pub kind: PersistKind,
pub key: &'a str,
}
impl<'a> PersistKey<'a> {
pub fn new(realm: &str, kind: PersistKind, key: &'a str) -> Self {
let mut h = DefaultHasher::new();
realm.hash(&mut h);
let realm = h.finish();
PersistKey { realm, kind, key }
}
}
impl<'a> std::fmt::Display for PersistKey<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}_{}_{}",
self.realm,
self.kind.name(),
self.key.replace('.', "_").replace('*', "STAR")
)
}
}
pub trait Persist: Clone + Send {
fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()>;
fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>>;
}
#[derive(Clone, Default)]
pub struct MemoryPersist {
inner: Arc<Mutex<HashMap<String, Vec<u8>>>>,
}
impl MemoryPersist {
pub fn new() -> Self {
MemoryPersist {
..Default::default()
}
}
}
impl Persist for MemoryPersist {
fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()> {
let mut lock = self.inner.lock().unwrap();
lock.insert(key.to_string(), value.to_owned());
Ok(())
}
fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>> {
let lock = self.inner.lock().unwrap();
Ok(lock.get(&key.to_string()).cloned())
}
}
#[derive(Clone)]
pub struct FilePersist {
dir: PathBuf,
}
impl FilePersist {
pub fn new<P: AsRef<Path>>(dir: P) -> Self {
FilePersist {
dir: dir.as_ref().to_path_buf(),
}
}
}
impl Persist for FilePersist {
#[cfg(not(unix))]
fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()> {
let f_name = file_name_of(&self.dir, &key);
fs::write(f_name, value).map_err(Error::from)
}
#[cfg(unix)]
fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()> {
let f_name = file_name_of(&self.dir, key);
match key.kind {
PersistKind::AccountPrivateKey | PersistKind::PrivateKey => fs::OpenOptions::new()
.mode(0o600)
.write(true)
.truncate(true)
.create(true)
.open(f_name)?
.write_all(value)
.map_err(Error::from),
PersistKind::Certificate => fs::write(f_name, value).map_err(Error::from),
}
}
fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>> {
let f_name = file_name_of(&self.dir, key);
let ret = if let Ok(mut file) = fs::File::open(f_name) {
let mut v = vec![];
file.read_to_end(&mut v)?;
Some(v)
} else {
None
};
Ok(ret)
}
}
fn file_name_of(dir: &Path, key: &PersistKey) -> PathBuf {
let mut f_name = dir.join(key.to_string());
f_name.set_extension(key.kind.name());
f_name
}