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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! # Storage for `KeyFiles` and `Contracts`

mod keyfile;

pub use self::KeyStorageError;
pub use self::keyfile::*;
use log::LogLevel;
use std::{env, fs};
use std::boxed::Box;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};

/// Base dir for internal data, all chain-related should be store in subdirectories
#[derive(Debug, Clone)]
pub struct Storages {
    /// base dir
    base_dir: PathBuf,
}

/// Default path (*nix)
#[cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))]
pub fn default_path() -> PathBuf {
    let mut config_dir = env::home_dir().expect("Expect path to home dir");
    config_dir.push(".emerald");
    config_dir
}

/// Default path (Mac OS X)
#[cfg(target_os = "macos")]
pub fn default_path() -> PathBuf {
    let mut config_dir = env::home_dir().expect("Expect path to home dir");
    config_dir.push("Library");
    config_dir.push("Emerald");
    config_dir
}

/// Default path (Windows OS)
#[cfg(target_os = "windows")]
pub fn default_path() -> PathBuf {
    let app_data_var = env::var("APPDATA").expect("Expect 'APPDATA' environment variable");
    let mut config_dir = PathBuf::from(app_data_var);
    config_dir.push(".emerald");
    config_dir
}

/// Default path for `Keystore` files
pub fn default_keystore_path(chain_id: &str) -> PathBuf {
    let mut path = default_path();
    path.push(chain_id);
    path.push("keystore");
    path
}

/// Creates specific type of storage (database or filesystem)
pub fn build_storage<P>(keystore_path: P) -> Result<Box<KeyfileStorage>, KeyStorageError>
where
    P: AsRef<Path>,
{
    #[cfg(feature = "default")]
    {
        let mut p = PathBuf::new();
        p.push(keystore_path);
        p.push(".db");
        match DbStorage::new(p) {
            Ok(db) => Ok(Box::new(db)),
            Err(_) => Err(KeyStorageError::StorageError(
                "Can't create database Keyfile storage".to_string(),
            )),
        }
    }
    #[cfg(feature = "fs-storage")]
    match FsStorage::new(keystore_path) {
        Ok(fs) => Ok(Box::new(fs)),
        Err(_) => Err(KeyStorageError::StorageError(
            "Can't create filesystem Keyfile storage".to_string(),
        )),
    }
}

impl Storages {
    /// Create storage using user directory if specified, or default path in other case.
    pub fn new(path: PathBuf) -> Storages {
        Storages { base_dir: path }
    }

    /// Initialize new storage
    pub fn init(&self) -> Result<(), Error> {
        if !&self.base_dir.exists() {
            if log_enabled!(LogLevel::Info) {
                info!("Init new storage at {}", self.base_dir.display());
            }
            fs::create_dir(self.base_dir.as_path())?
        }
        Ok(())
    }

    /// Get keystore storage by chain name
    pub fn get_keystore_path(&self, chain_name: &str) -> Result<PathBuf, Error> {
        for entry in fs::read_dir(&self.base_dir)? {
            let entry = entry?;
            let mut path = entry.path();

            if path.is_dir() && path.file_name().is_some() &&
                path.file_name().unwrap() == chain_name
            {
                path.push("keystore");
                return Ok(path);

            }
        }

        Err(Error::new(
            ErrorKind::InvalidInput,
            "No keystorage for specified chain name",
        ))
    }
}

impl Default for Storages {
    fn default() -> Self {
        Storages { base_dir: default_path() }
    }
}

/// Subdir for a chain
#[derive(Debug, Clone)]
pub struct ChainStorage<'a> {
    /// subdir name
    pub id: String,
    /// storage
    base: &'a Storages,
}

impl<'a> ChainStorage<'a> {
    /// Crate a new chain
    pub fn new(base: &'a Storages, id: String) -> ChainStorage<'a> {
        ChainStorage { id: id, base: base }
    }

    /// Initialize a new chain
    pub fn init(&self) -> Result<(), Error> {
        let mut p: PathBuf = self.base.base_dir.to_path_buf();
        p.push(self.id.clone());
        if !p.exists() {
            if log_enabled!(LogLevel::Info) {
                info!("Init new chain at {}", p.display());
            }
            fs::create_dir(p)?
        }

        let ks_path = default_keystore_path(&self.id);
        if !ks_path.exists() {
            fs::create_dir(ks_path.as_path())?
        }

        Ok(())
    }

    /// Get chain path
    pub fn get_path(&self, id: String) -> Result<PathBuf, Error> {
        let mut p: PathBuf = self.base.base_dir.to_path_buf().clone();
        p.push(self.id.clone());
        p.push(id.clone());
        if !p.exists() {
            if log_enabled!(LogLevel::Debug) {
                debug!("Init new chain storage at {}", p.display());
            }
            fs::create_dir(&p)?
        }
        Ok(p)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn should_use_default_path() {
        let st = Storages::default();
        assert_eq!(st.base_dir, default_path());
    }

    #[test]
    fn should_use_user_path() {
        let user_path: &str = "/tmp/some";
        let st = Storages::new(PathBuf::from(user_path));

        assert_eq!(st.base_dir, PathBuf::from(user_path));
    }
}