use super::toml;
use super::config::Config;
use super::node::Node;
use std::io;
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
use std::fs::File;
#[derive(Deserialize, Serialize)]
pub struct StorageState {
last_id: u64,
}
pub struct Storage<'a> {
config: &'a Config,
name: String,
path: PathBuf,
state: StorageState
}
#[derive(Debug)]
pub enum LoadStorageError {
InvalidName,
NotFound,
Open(io::Error),
Read(io::Error),
Parse(toml::de::Error)
}
impl<'a> Storage<'a> {
pub fn load(config: &'a Config, name: &str, path: PathBuf)
-> Result<Storage<'a>, LoadStorageError> {
let mut spath = path.clone();
spath.push("storage");
let mut f = match File::open(spath) {
Ok(f) => f,
Err(e) => return Err(LoadStorageError::Open(e)),
};
let mut s = String::new();
if let Err(e) = f.read_to_string(&mut s) {
return Err(LoadStorageError::Read(e));
}
let state = match toml::from_str::<StorageState>(&s) {
Ok(s) => s,
Err(e) => return Err(LoadStorageError::Parse(e)),
};
Ok(Storage { config, name: name.to_string(), path, state })
}
pub fn next_id(&self) -> u64 {
self.state.last_id + 1
}
pub fn use_id(&mut self) {
self.state.last_id += 1;
}
pub fn path(&self) -> &PathBuf {
&self.path
}
pub fn nodes_path(&self) -> PathBuf {
let mut path = self.path.clone();
path.push("nodes");
path
}
pub fn config(&self) -> &Config {
self.config
}
pub fn name(&self) -> &str {
&self.name
}
pub fn nodes(&self) -> Vec<Node> {
let dir = match fs::read_dir(self.nodes_path()) {
Ok(a) => a,
Err(e) => {
println!("Failed to read nodes dir of storage {}: {}",
self.name, e);
return Vec::new();
},
};
let mut nodes = Vec::new();
for entry in dir {
let entry = match entry {
Ok(a) => a,
Err(e) => {
println!("Invalid nodes entry in storage {}: {}",
self.name, e);
continue;
},
};
let entry = entry.path();
if entry.is_dir() {
continue;
}
let id = entry.file_stem()
.and_then(|f| f.to_str())
.and_then(|f| f.parse::<u64>().ok());
match id {
Some(id) => nodes.push(Node::new(&self, id)),
None => println!("Invalid node file in {}: {}",
self.name, entry.to_str().unwrap_or("<invalid>")),
}
}
nodes
}
pub fn next_node(&self) -> Node {
Node::new(self, self.next_id())
}
}
impl<'a> Drop for Storage<'a> {
fn drop(&mut self) {
let mut path = self.path.clone();
path.push("storage");
let mut f = match File::create(path) {
Ok(f) => f,
Err(err) => {
println!("Failed to create file to write \
storage state: {}", err);
return;
}
};
if let Err(err) = f.write_all(toml::to_string(&self.state)
.expect("Internal error, deserializing state file")
.as_bytes()) {
println!("Failed to write initial config file: {}", err);
}
}
}