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
// Copyright 2018 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

use std::collections::BTreeMap;

use std::collections::btree_map::Entry;

use std::path::{Path, PathBuf};

use std::fs;

use std::sync::{Arc, Mutex, RwLock};

use error::Error;

use store::Store;

use config::Config;

pub type Handle = Arc<RwLock<Store>>;

/// A process is only permitted to have one open handle to each database. This manager
/// exists to enforce that constraint: don't open databases directly.
pub struct Manager {
    stores: Mutex<BTreeMap<PathBuf, Handle>>,
}

impl Manager {
    /// Create a new store manager
    pub fn new() -> Manager {
        Manager {
            stores: Mutex::new(Default::default()),
        }
    }

    /// Return the open store at `path`, returning `None` if it has not already been opened.
    pub fn get<'p, P>(&self, path: P) -> Result<Option<Handle>, Error>
    where
        P: AsRef<Path>,
    {
        let canonical = path.as_ref().canonicalize()?;
        Ok(self.stores.lock().unwrap().get(&canonical).cloned())
    }

    /// Return the open store at cfg.path, or create it using the given config.
    pub fn open(&mut self, cfg: Config) -> Result<Handle, Error> {
        let _ = fs::create_dir_all(&cfg.path);
        let canonical = cfg.path.as_path().canonicalize()?;
        let mut map = self.stores.lock().unwrap();
        Ok(match map.entry(canonical) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let k = Arc::new(RwLock::new(Store::new(cfg)?));
                e.insert(k).clone()
            }
        })
    }

    /// Load a store from a configuration file
    pub fn load_config_and_open<P>(&mut self, path: P) -> Result<Handle, Error>
    where
        P: AsRef<Path>,
    {
        let config = Config::load(path)?;
        self.open(config)
    }
}