extern crate any_cache;
extern crate notify;
use any_cache::{Cache, HashCache};
pub use any_cache::CacheKey;
use notify::{Op, RawEvent, RecommendedWatcher, RecursiveMode, Watcher, raw_watcher};
use notify::op::WRITE;
use std::cell::RefCell;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::hash;
use std::io;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::mpsc::{Receiver, channel};
use std::time::{Duration, Instant};
pub trait Load: 'static + Sized {
type Key: Into<DepKey>;
type Error: Error;
fn load(key: Self::Key, store: &mut Store) -> Result<Loaded<Self>, Self::Error>;
fn reload(&self, key: Self::Key, store: &mut Store) -> Result<Self, Self::Error> {
Self::load(key, store).map(|lr| lr.res)
}
}
pub struct Loaded<T> {
pub res: T,
pub deps: Vec<DepKey>
}
impl<T> Loaded<T> {
pub fn without_dep(res: T) -> Self {
Loaded { res, deps: Vec::new() }
}
pub fn with_deps(res: T, deps: Vec<DepKey>) -> Self {
Loaded { res, deps }
}
}
impl<T> From<T> for Loaded<T> {
fn from(res: T) -> Self {
Loaded::without_dep(res)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum DepKey {
Path(PathKey),
Logical(LogicalKey)
}
impl From<PathKey> for DepKey {
fn from(key: PathKey) -> Self {
DepKey::Path(key)
}
}
impl From<LogicalKey> for DepKey {
fn from(key: LogicalKey) -> Self {
DepKey::Logical(key)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct PathKey(PathBuf);
impl PathKey {
pub fn as_path(&self) -> &Path {
&self.0
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct LogicalKey(String);
impl LogicalKey {
pub fn as_str(&self) -> &str {
&self.0
}
}
pub type Res<T> = Rc<RefCell<T>>;
pub struct Key<T> where T: Load {
inner: T::Key,
_t: PhantomData<*const T>
}
impl<T> Key<T> where T: Load<Key = PathKey> {
pub fn path<P>(path: P) -> io::Result<Self> where P: AsRef<Path> {
let canon_path = path.as_ref().canonicalize()?;
Ok(Key {
inner: PathKey(canon_path),
_t: PhantomData
})
}
pub fn as_path(&self) -> &Path {
self.inner.as_path()
}
}
impl<T> Key<T> where T: Load<Key = LogicalKey> {
pub fn logical(id: &str) -> Self {
Key {
inner: LogicalKey(id.to_owned()),
_t: PhantomData
}
}
pub fn as_str(&self) -> &str {
self.inner.as_str()
}
}
impl<T> Clone for Key<T> where T: Load, T::Key: Clone {
fn clone(&self) -> Self {
Key {
inner: self.inner.clone(),
_t: PhantomData
}
}
}
impl<T> fmt::Debug for Key<T> where T: Load, T::Key: fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
self.inner.fmt(f)
}
}
impl<T> Eq for Key<T> where T: Load, T::Key: Eq {}
impl<T> hash::Hash for Key<T> where T: Load, T::Key: hash::Hash {
fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
self.inner.hash(state)
}
}
impl<T> PartialEq for Key<T> where T: Load, T::Key: PartialEq {
fn eq(&self, rhs: &Self) -> bool {
self.inner.eq(&rhs.inner)
}
}
impl<T> CacheKey for Key<T> where T: 'static + Load, T::Key: hash::Hash {
type Target = Res<T>;
}
impl<T> From<Key<T>> for DepKey where T: Load {
fn from(key: Key<T>) -> Self {
key.inner.into()
}
}
pub struct Store {
opt: StoreOpt,
canon_root: PathBuf,
cache: HashCache,
metadata: HashMap<DepKey, ResMetaData>,
deps: HashMap<DepKey, Vec<DepKey>>,
#[allow(dead_code)]
watcher: RecommendedWatcher,
watcher_rx: Receiver<RawEvent>,
}
impl Store {
pub fn new(opt: StoreOpt) -> Result<Self, StoreError> {
let root = opt.root().to_owned();
let canon_root = root.canonicalize().map_err(|_| StoreError::RootDoesDotExit(root))?;
let (wsx, wrx) = channel();
let mut watcher = raw_watcher(wsx).unwrap();
let _ = watcher.watch(&canon_root, RecursiveMode::Recursive);
Ok(Store {
opt,
canon_root,
cache: HashCache::new(),
metadata: HashMap::new(),
deps: HashMap::new(),
watcher,
watcher_rx: wrx,
})
}
pub fn root(&self) -> &Path {
&self.canon_root
}
fn inject<T>(
&mut self,
key: Key<T>,
resource: T,
deps: Vec<DepKey>
) -> Result<Res<T>, StoreError>
where T: Load,
T::Key: Clone + hash::Hash {
let inner_key = key.inner.clone();
let dep_key = inner_key.clone().into();
if self.metadata.contains_key(&dep_key) {
return Err(StoreError::AlreadyRegisteredKey(dep_key.clone()));
}
let res = Rc::new(RefCell::new(resource));
let res_ = res.clone();
let on_reload: Box<for<'a> Fn(&'a mut Store) -> Result<(), Box<Error>>> = Box::new(move |store| {
let reloaded = T::reload(&res_.borrow(), inner_key.clone(), store);
match reloaded {
Ok(r) => {
*res_.borrow_mut() = r;
Ok(())
},
Err(e) => Err(Box::new(e))
}
});
let metadata = ResMetaData {
on_reload: on_reload,
last_update_instant: Instant::now(),
};
self.cache.save(key, res.clone());
self.metadata.insert(dep_key.clone(), metadata);
for dep in deps {
self.deps.entry(dep.clone()).or_insert(Vec::new()).push(dep_key.clone());
}
Ok(res)
}
pub fn get<T>(
&mut self,
key: &Key<T>
) -> Result<Res<T>, StoreErrorOr<T>>
where T: Load,
T::Key: Clone + hash::Hash {
match self.cache.get(key).cloned() {
Some(resource) => {
Ok(resource)
},
None => {
let loaded = T::load(key.inner.clone(), self).map_err(StoreErrorOr::ResError)?;
self.inject(key.clone(), loaded.res, loaded.deps).map_err(StoreErrorOr::StoreError)
}
}
}
pub fn get_proxied<T, P>(
&mut self,
key: &Key<T>,
proxy: P
) -> Result<Res<T>, StoreError>
where T: Load,
T::Key: Clone + hash::Hash,
P: FnOnce() -> T {
self.get(key).or(self.inject(key.clone(), proxy(), Vec::new()))
}
pub fn sync(&mut self) {
let update_await_time_ms = self.opt.update_await_time_ms();
let dep_keys = dequeue_file_changes(&mut self.watcher_rx);
for dep_key in dep_keys {
if let Some(mut metadata) = self.metadata.remove(&dep_key) {
let now = Instant::now();
if now.duration_since(metadata.last_update_instant) >= Duration::from_millis(update_await_time_ms) {
if (metadata.on_reload)(self).is_ok() {
if let Some(deps) = self.deps.get(&dep_key).cloned() {
for dep in deps {
if let Some(obs_metadata) = self.metadata.remove(&dep) {
let _ = (obs_metadata.on_reload)(self);
self.metadata.insert(dep, obs_metadata);
}
}
}
}
}
metadata.last_update_instant = now;
self.metadata.insert(dep_key, metadata);
}
}
}
}
pub struct StoreOpt {
root: PathBuf,
update_await_time_ms: u64
}
impl Default for StoreOpt {
fn default() -> Self {
StoreOpt {
root: PathBuf::from("."),
update_await_time_ms: 1000
}
}
}
impl StoreOpt {
#[inline]
pub fn set_update_await_time_ms(self, ms: u64) -> Self {
StoreOpt {
update_await_time_ms: ms,
.. self
}
}
#[inline]
pub fn update_await_time_ms(&self) -> u64 {
self.update_await_time_ms
}
#[inline]
pub fn set_root<P>(self, root: P) -> Self where P: AsRef<Path> {
StoreOpt {
root: root.as_ref().to_owned(),
.. self
}
}
#[inline]
pub fn root(&self) -> &Path {
&self.root
}
}
struct ResMetaData {
on_reload: Box<Fn(&mut Store) -> Result<(), Box<Error>>>,
last_update_instant: Instant,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StoreError {
RootDoesDotExit(PathBuf),
AlreadyRegisteredKey(DepKey)
}
impl fmt::Display for StoreError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(self.description())
}
}
impl Error for StoreError {
fn description(&self) -> &str {
match *self {
StoreError::RootDoesDotExit(_) => "root doesn’t exist",
StoreError::AlreadyRegisteredKey(_) => "already registered key"
}
}
}
pub enum StoreErrorOr<T> where T: Load {
StoreError(StoreError),
ResError(T::Error)
}
impl<T> Clone for StoreErrorOr<T> where T: Load, T::Error: Clone {
fn clone(&self) -> Self {
match *self {
StoreErrorOr::StoreError(ref e) => StoreErrorOr::StoreError(e.clone()),
StoreErrorOr::ResError(ref e) => StoreErrorOr::ResError(e.clone())
}
}
}
impl<T> Eq for StoreErrorOr<T> where T: Load, T::Error: Eq {}
impl<T> PartialEq for StoreErrorOr<T> where T: Load, T::Error: PartialEq {
fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) {
(&StoreErrorOr::StoreError(ref a), &StoreErrorOr::StoreError(ref b)) => a == b,
(&StoreErrorOr::ResError(ref a), &StoreErrorOr::ResError(ref b)) => a == b,
_ => false
}
}
}
impl<T> fmt::Debug for StoreErrorOr<T> where T: Load, T::Error: fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
StoreErrorOr::StoreError(ref e) => f.debug_tuple("StoreError").field(e).finish(),
StoreErrorOr::ResError(ref e) => f.debug_tuple("ResError").field(e).finish()
}
}
}
impl<T> fmt::Display for StoreErrorOr<T> where T: Load, T::Error: fmt::Debug {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str(self.description())
}
}
impl<T> Error for StoreErrorOr<T> where T: Load, T::Error: fmt::Debug {
fn description(&self) -> &str {
match *self {
StoreErrorOr::StoreError(ref e) => e.description(),
StoreErrorOr::ResError(ref e) => e.description()
}
}
fn cause(&self) -> Option<&Error> {
match *self {
StoreErrorOr::StoreError(ref e) => e.cause(),
StoreErrorOr::ResError(ref e) => e.cause()
}
}
}
fn dequeue_file_changes(rx: &mut Receiver<RawEvent>) -> Vec<DepKey> {
rx.try_iter().filter_map(|event| {
match event {
RawEvent { path: Some(ref path), op: Ok(op), .. } if op | WRITE != Op::empty() => {
Some(DepKey::Path(PathKey(path.to_owned())))
},
_ => None
}
}).collect()
}