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
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

// Copyright 2021 Conflux Foundation. All rights reserved.
// Conflux is free software and distributed under GNU General Public License.
// See http://www.gnu.org/licenses/

use crate::{CryptoKVStorage, Error, GetResponse, KVStorage, Storage};
use serde::{de::DeserializeOwned, Serialize};

/// This provides a light wrapper around KV storages to support a namespace.
/// That namespace is effectively prefixing all keys with then namespace value
/// and "/" so a namespace of foo and a key of bar becomes "foo/bar". Without a
/// namespace, the key would just be "bar". This matches how this library
/// implements namespaces for Vault.
pub struct NamespacedStorage {
    namespace: String,
    inner: Box<Storage>,
}

impl KVStorage for NamespacedStorage {
    fn available(&self) -> Result<(), Error> { self.inner.available() }

    fn get<T: DeserializeOwned>(
        &self, key: &str,
    ) -> Result<GetResponse<T>, Error> {
        self.inner.get(&self.ns_name(key))
    }

    fn set<T: Serialize>(&mut self, key: &str, value: T) -> Result<(), Error> {
        self.inner.set(&self.ns_name(key), value)
    }

    /// Note: This is not a namespace function
    #[cfg(any(test, feature = "testing"))]
    fn reset_and_clear(&mut self) -> Result<(), Error> {
        self.inner.reset_and_clear()
    }
}

impl NamespacedStorage {
    pub fn new(storage: Storage, namespace: String) -> Self {
        NamespacedStorage {
            namespace,
            inner: Box::new(storage),
        }
    }

    fn ns_name(&self, key: &str) -> String {
        format!("{}/{}", self.namespace, key)
    }
}

impl CryptoKVStorage for NamespacedStorage {}

#[cfg(test)]
mod test {
    use super::*;
    use crate::OnDiskStorage;
    use diem_temppath::TempPath;

    #[test]
    fn test_different_namespaces() {
        let ns0 = "ns0";
        let ns1 = "ns1";
        let key = "key";

        let path_buf = TempPath::new().path().to_path_buf();

        let mut default = OnDiskStorage::new(path_buf.clone());

        let storage =
            Storage::OnDiskStorage(OnDiskStorage::new(path_buf.clone()));
        let mut nss0 = NamespacedStorage::new(storage, ns0.into());

        let storage = Storage::OnDiskStorage(OnDiskStorage::new(path_buf));
        let mut nss1 = NamespacedStorage::new(storage, ns1.into());

        default.set(key, 0).unwrap();
        nss0.set(key, 1).unwrap();
        nss1.set(key, 2).unwrap();

        assert_eq!(default.get::<u64>(key).unwrap().value, 0);
        assert_eq!(nss0.get::<u64>(key).unwrap().value, 1);
        assert_eq!(nss1.get::<u64>(key).unwrap().value, 2);
    }

    #[test]
    fn test_shared_namespace() {
        let ns = "ns";
        let key = "key";

        let path_buf = TempPath::new().path().to_path_buf();

        let default =
            Storage::OnDiskStorage(OnDiskStorage::new(path_buf.clone()));

        let storage =
            Storage::OnDiskStorage(OnDiskStorage::new(path_buf.clone()));
        let mut nss = NamespacedStorage::new(storage, ns.into());

        let storage = Storage::OnDiskStorage(OnDiskStorage::new(path_buf));
        let another_nss = NamespacedStorage::new(storage, ns.into());

        nss.set(key, 1).unwrap();
        default.get::<u64>(key).unwrap_err();
        assert_eq!(nss.get::<u64>(key).unwrap().value, 1);
        assert_eq!(another_nss.get::<u64>(key).unwrap().value, 1);
    }
}