use lockchain::traits::{AutoEncoder, Body, FileIO, Vault}; use std::collections::{HashMap, HashSet}; use std::marker::PhantomData; use std::path::PathBuf; /// An in-memory API state object which is delegated to all handlers /// /// This mechanism serves two purposes /// /// 1. Configuration of the API, beyond simple paramters provided to /// the server_start call /// 2. Buffering and pre-loading of certain vault components that need /// to be accessed via the handlers /// /// It provides some simple query functions for handlers to work on, /// as well as expose raw configuration fields to be written /// /// ```norun /// let state: ApiState = ApiState { /// bound_scope: false, /// working_dir: ".".into(), /// .. /// }; /// ``` /// /// (Replace `B` and `V` with your generics 🙂) pub struct ApiState where B: Body, V: Vault, { #[doc(hidden)] pub vaults: HashMap>, #[doc(hidden)] pub _phantom: PhantomData, #[doc(hidden)] pub tokens: HashSet, /// Signal if the API handlers are allowed outside their working dir pub bound_scope: bool, /// Provide a working directory pub working_dir: PathBuf, /// Completely disabe administrative actions pub administrative: bool, } impl ApiState where B: Body, V: Vault, { /// Load an existing API state from an encoded string pub fn load(encoded: &str) -> Option { SerializedState::decode(encoded).ok().map(|s| s.into()) } /// Store an in-memory API state to an encoded string pub fn store(&self) -> String { SerializedState::from(self).encode().ok().unwrap() } /// Return a list of string slices for each vault in scope pub fn vaults(&self) -> Vec<&str> { self.vaults.iter().map(|(k, _)| k.as_str()).collect() } /// Simply return the number of known vaults pub fn count(&self) -> usize { self.vaults.len() } pub fn add_vault(&mut self, name: &str, vault: V) { self.vaults.insert(name.into(), Some(vault)); } pub fn get_vault(&mut self, name: &str) -> Option<&mut V> { self.vaults.get_mut(name)?.as_mut() } } impl Default for ApiState where B: Body, V: Vault, { #[allow(unconditional_recursion)] fn default() -> Self { Self { _phantom: PhantomData, bound_scope: true, vaults: HashMap::new(), tokens: HashSet::new(), administrative: false, ..Default::default() } } } #[derive(Serialize, Deserialize)] struct SerializedState { vaults: Vec, } impl AutoEncoder for SerializedState {} impl FileIO for SerializedState {} /// Implements the transform from in-memory to on-disk impl<'state, B, V> From<&'state ApiState> for SerializedState where B: Body, V: Vault, { fn from(me: &'state ApiState) -> Self { Self { vaults: me.vaults.iter().map(|(k, _)| k.clone()).collect(), } } } /// Implements the transform from on-disk to in-memory impl From for ApiState where B: Body, V: Vault, { fn from(me: SerializedState) -> Self { Self { vaults: me.vaults.into_iter().map(|k| (k, None)).collect(), _phantom: PhantomData, ..Default::default() } } }