aboutsummaryrefslogtreecommitdiff
path: root/lockchain-http/src/state.rs
blob: 718c690bdb9d5a633dd634bd2eebba59dab5d329 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use crate::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<B, V> = ApiState {
///     bound_scope: false,
///     working_dir: ".".into(),
///     ..
/// };
/// ```
///
/// (Replace `B` and `V` with your generics 🙂)
pub struct ApiState<B, V>
where
    B: Body,
    V: Vault<B>,
{
    #[doc(hidden)]
    pub vaults: HashMap<String, Option<V>>,
    #[doc(hidden)]
    pub _phantom: PhantomData<B>,
    #[doc(hidden)]
    pub tokens: HashSet<String>,
    /// 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<B, V> ApiState<B, V>
where
    B: Body,
    V: Vault<B>,
{
    /// Load an existing API state from an encoded string
    pub fn load(encoded: &str) -> Option<Self> {
        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<B, V> Default for ApiState<B, V>
where
    B: Body,
    V: Vault<B>,
{
    #[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<String>,
}

impl AutoEncoder for SerializedState {}
impl FileIO for SerializedState {}

/// Implements the transform from in-memory to on-disk
impl<'state, B, V> From<&'state ApiState<B, V>> for SerializedState
where
    B: Body,
    V: Vault<B>,
{
    fn from(me: &'state ApiState<B, V>) -> Self {
        Self {
            vaults: me.vaults.iter().map(|(k, _)| k.clone()).collect(),
        }
    }
}

/// Implements the transform from on-disk to in-memory
impl<B, V> From<SerializedState> for ApiState<B, V>
where
    B: Body,
    V: Vault<B>,
{
    fn from(me: SerializedState) -> Self {
        Self {
            vaults: me.vaults.into_iter().map(|k| (k, None)).collect(),
            _phantom: PhantomData,
            ..Default::default()
        }
    }
}