2020-01-06 16:32:56 +00:00
|
|
|
//! A storage container for client data
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
use log::{debug, error, info, trace, warn};
|
|
|
|
|
2020-02-02 04:40:07 +00:00
|
|
|
use std::fmt::Debug;
|
2020-01-26 00:05:12 +00:00
|
|
|
use std::path::Path;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{BufReader, Read};
|
|
|
|
|
|
|
|
use toml::Value as TomlValue;
|
|
|
|
|
2020-01-13 00:22:46 +00:00
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
|
2020-01-06 16:32:56 +00:00
|
|
|
use quick_error::quick_error;
|
|
|
|
|
|
|
|
quick_error! {
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum CofferError {
|
2020-01-08 22:03:49 +00:00
|
|
|
Msg(err: &'static str) {
|
|
|
|
from(err)
|
2020-01-19 10:31:33 +00:00
|
|
|
display("{}", err)
|
2020-01-08 22:03:49 +00:00
|
|
|
}
|
|
|
|
Other(err: Box<dyn std::error::Error>) {
|
|
|
|
cause(&**err)
|
|
|
|
}
|
2020-01-06 16:32:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type CofferResult<T> = Result<T, CofferError>;
|
|
|
|
|
|
|
|
/// Values supported by a `Coffer`
|
2020-01-13 00:22:46 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2020-01-06 16:32:56 +00:00
|
|
|
pub enum CofferValue {
|
|
|
|
/// A UTF-8 encoded string
|
|
|
|
String(String),
|
|
|
|
/// A 32-bit integer
|
|
|
|
Integer(i32),
|
2020-01-26 00:05:12 +00:00
|
|
|
/// A 32-bit float
|
|
|
|
Float(f32),
|
|
|
|
// A boolean
|
|
|
|
Boolean(bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
|
|
pub struct CofferKey {
|
|
|
|
pub shard: String,
|
|
|
|
pub key: String
|
2020-01-06 16:32:56 +00:00
|
|
|
}
|
|
|
|
|
2020-01-26 00:05:12 +00:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct CofferShard(pub Vec<(String, CofferValue)>);
|
2020-01-06 16:32:56 +00:00
|
|
|
|
|
|
|
/// Interface for interacting with a `Coffer`
|
|
|
|
pub trait Coffer {
|
|
|
|
/// Put `value` at `path`. Errors if there is already a value at `path`.
|
2020-01-26 00:05:12 +00:00
|
|
|
fn put(&mut self, key: CofferKey, value: CofferValue) -> CofferResult<()>;
|
2020-01-08 22:03:49 +00:00
|
|
|
|
2020-01-19 10:31:33 +00:00
|
|
|
/// Push `value` to `path`. Replaces existing values.
|
2020-01-26 00:05:12 +00:00
|
|
|
fn push(&mut self, key: CofferKey, value: CofferValue);
|
2020-01-08 22:03:49 +00:00
|
|
|
|
2020-01-06 16:32:56 +00:00
|
|
|
/// Retrieve `value` at path. Errors if there is no `value` at path.
|
2020-01-26 00:05:12 +00:00
|
|
|
fn get(&self, key: &CofferKey) -> CofferResult<CofferValue>;
|
|
|
|
|
|
|
|
/// Retrieve `value` at path. Errors if there is no `value` at path.
|
|
|
|
fn get_shard<T>(&self, shard: T) -> CofferResult<CofferShard>
|
|
|
|
where T: AsRef<str>;
|
2020-01-08 22:03:49 +00:00
|
|
|
|
2020-01-26 00:05:12 +00:00
|
|
|
fn from_toml_file(toml_path: &Path) -> Self
|
|
|
|
where Self: Coffer + Default
|
|
|
|
{
|
|
|
|
// read the secrets file into a temporary string
|
|
|
|
let mut file = BufReader::new(File::open(toml_path).unwrap());
|
|
|
|
let mut secrets_buf = String::new();
|
|
|
|
file.read_to_string(&mut secrets_buf).unwrap();
|
|
|
|
|
|
|
|
Coffer::from_toml(&secrets_buf)
|
2020-01-08 22:03:49 +00:00
|
|
|
}
|
2020-01-06 16:32:56 +00:00
|
|
|
|
2020-01-26 00:05:12 +00:00
|
|
|
fn from_toml(toml: &str) -> Self
|
|
|
|
where Self: Coffer + Default
|
|
|
|
{
|
|
|
|
// call implementation to create an empty coffer
|
|
|
|
let mut coffer = Self::default();
|
|
|
|
|
|
|
|
// parse the string into a toml Table
|
|
|
|
let clients: toml::value::Table = match toml.parse::<TomlValue>().unwrap() {
|
|
|
|
TomlValue::Table(t) => t,
|
|
|
|
_ => panic!{"Invalid secrets file"}
|
|
|
|
};
|
|
|
|
|
2020-02-02 04:40:07 +00:00
|
|
|
coffer.from_toml_table(&clients);
|
|
|
|
|
|
|
|
coffer
|
|
|
|
}
|
|
|
|
|
|
|
|
fn from_toml_table(&mut self, toml_table: &toml::value::Table) {
|
|
|
|
// table has an no id, recourse into subtables
|
|
|
|
if toml_table.get("id").is_none() {
|
|
|
|
for (_key, val) in toml_table.iter() {
|
|
|
|
match val {
|
|
|
|
TomlValue::Table(subtable) => {
|
|
|
|
self.from_toml_table(subtable);
|
|
|
|
},
|
|
|
|
_ => panic!{"Invalid secrets file"}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-01-26 00:05:12 +00:00
|
|
|
/*
|
2020-02-02 04:40:07 +00:00
|
|
|
* Parse a single shard/table, this is known to have an id
|
2020-01-26 00:05:12 +00:00
|
|
|
*
|
|
|
|
* [files]
|
2020-02-02 04:40:07 +00:00
|
|
|
* id = "ABC-DEF-GHE"
|
2020-01-26 00:05:12 +00:00
|
|
|
* secret_string = "secret value1"
|
|
|
|
* secret_int = 12345
|
|
|
|
* secret_bool = true
|
|
|
|
*/
|
2020-02-02 04:40:07 +00:00
|
|
|
let shard = toml_table.get("id").and_then(|id| id.as_str()).unwrap();
|
2020-01-26 00:05:12 +00:00
|
|
|
|
2020-02-02 04:40:07 +00:00
|
|
|
for (key, val) in toml_table {
|
|
|
|
if "id" == key { continue } // ids are for sharding
|
2020-01-26 00:05:12 +00:00
|
|
|
|
2020-02-02 04:40:07 +00:00
|
|
|
let value = match val {
|
|
|
|
TomlValue::String(s) => CofferValue::String(s.to_owned()),
|
|
|
|
TomlValue::Integer(i) => CofferValue::Integer(*i as i32),
|
|
|
|
TomlValue::Float(f) => CofferValue::Float(*f as f32),
|
|
|
|
TomlValue::Boolean(b) => CofferValue::Boolean(*b),
|
|
|
|
_ => panic!{"Value {:?} unsupported", val}
|
|
|
|
};
|
2020-01-26 00:05:12 +00:00
|
|
|
|
2020-02-02 04:40:07 +00:00
|
|
|
let key = key.to_owned();
|
|
|
|
let shard = shard.to_string();
|
|
|
|
self.put(CofferKey{shard, key}, value).unwrap();
|
2020-01-26 00:05:12 +00:00
|
|
|
}
|
2020-01-06 16:32:56 +00:00
|
|
|
}
|
|
|
|
}
|