2020-06-15 06:29:07 -05:00
|
|
|
//! Object safe interface for file watching and reading.
|
|
|
|
use std::fmt;
|
|
|
|
|
2020-06-24 08:52:07 -05:00
|
|
|
use paths::{AbsPath, AbsPathBuf};
|
2020-06-15 06:29:07 -05:00
|
|
|
|
2020-07-18 09:40:10 -05:00
|
|
|
#[derive(Debug, Clone)]
|
2020-06-15 06:29:07 -05:00
|
|
|
pub enum Entry {
|
|
|
|
Files(Vec<AbsPathBuf>),
|
2020-07-18 09:40:10 -05:00
|
|
|
Directories(Directories),
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Specifies a set of files on the file system.
|
|
|
|
///
|
|
|
|
/// A file is included if:
|
|
|
|
/// * it has included extension
|
|
|
|
/// * it is under an `include` path
|
|
|
|
/// * it is not under `exclude` path
|
|
|
|
///
|
|
|
|
/// If many include/exclude paths match, the longest one wins.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub struct Directories {
|
|
|
|
pub extensions: Vec<String>,
|
|
|
|
pub include: Vec<AbsPathBuf>,
|
|
|
|
pub exclude: Vec<AbsPathBuf>,
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
|
2020-06-11 04:04:09 -05:00
|
|
|
#[derive(Debug)]
|
2020-06-15 06:29:07 -05:00
|
|
|
pub struct Config {
|
|
|
|
pub load: Vec<Entry>,
|
|
|
|
pub watch: Vec<usize>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub enum Message {
|
2020-06-24 09:58:49 -05:00
|
|
|
Progress { n_total: usize, n_done: usize },
|
2020-06-15 06:29:07 -05:00
|
|
|
Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type Sender = Box<dyn Fn(Message) + Send>;
|
|
|
|
|
|
|
|
pub trait Handle: fmt::Debug {
|
|
|
|
fn spawn(sender: Sender) -> Self
|
|
|
|
where
|
|
|
|
Self: Sized;
|
|
|
|
fn set_config(&mut self, config: Config);
|
|
|
|
fn invalidate(&mut self, path: AbsPathBuf);
|
2020-06-24 08:52:07 -05:00
|
|
|
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>>;
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Entry {
|
|
|
|
pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
|
2020-07-18 09:40:10 -05:00
|
|
|
Entry::Directories(dirs(base, &[".git"]))
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
|
2020-07-18 09:40:10 -05:00
|
|
|
Entry::Directories(dirs(base, &[".git", "target"]))
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
|
2020-07-18 09:40:10 -05:00
|
|
|
Entry::Directories(dirs(base, &[".git", "/tests", "/examples", "/benches"]))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn contains_file(&self, path: &AbsPath) -> bool {
|
|
|
|
match self {
|
|
|
|
Entry::Files(files) => files.iter().any(|it| it == path),
|
|
|
|
Entry::Directories(dirs) => dirs.contains_file(path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn contains_dir(&self, path: &AbsPath) -> bool {
|
|
|
|
match self {
|
|
|
|
Entry::Files(_) => false,
|
|
|
|
Entry::Directories(dirs) => dirs.contains_dir(path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Directories {
|
|
|
|
pub fn contains_file(&self, path: &AbsPath) -> bool {
|
|
|
|
let ext = path.extension().unwrap_or_default();
|
|
|
|
if self.extensions.iter().all(|it| it.as_str() != ext) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
self.includes_path(path)
|
|
|
|
}
|
|
|
|
pub fn contains_dir(&self, path: &AbsPath) -> bool {
|
|
|
|
self.includes_path(path)
|
|
|
|
}
|
|
|
|
fn includes_path(&self, path: &AbsPath) -> bool {
|
2020-07-20 11:01:42 -05:00
|
|
|
let mut include: Option<&AbsPathBuf> = None;
|
2020-07-18 09:40:10 -05:00
|
|
|
for incl in &self.include {
|
2020-07-20 11:01:42 -05:00
|
|
|
if path.starts_with(incl) {
|
2020-07-18 09:40:10 -05:00
|
|
|
include = Some(match include {
|
2020-07-20 11:01:42 -05:00
|
|
|
Some(prev) if prev.starts_with(incl) => prev,
|
2020-07-18 09:40:10 -05:00
|
|
|
_ => incl,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let include = match include {
|
|
|
|
Some(it) => it,
|
|
|
|
None => return false,
|
|
|
|
};
|
|
|
|
for excl in &self.exclude {
|
2020-07-20 11:01:42 -05:00
|
|
|
if path.starts_with(excl) && excl.starts_with(include) {
|
2020-07-18 09:40:10 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-07-20 11:01:42 -05:00
|
|
|
true
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-18 09:40:10 -05:00
|
|
|
fn dirs(base: AbsPathBuf, exclude: &[&str]) -> Directories {
|
|
|
|
let exclude = exclude.iter().map(|it| base.join(it)).collect::<Vec<_>>();
|
|
|
|
Directories { extensions: vec!["rs".to_string()], include: vec![base], exclude }
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Debug for Message {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Message::Loaded { files } => {
|
|
|
|
f.debug_struct("Loaded").field("n_files", &files.len()).finish()
|
|
|
|
}
|
2020-06-24 09:58:49 -05:00
|
|
|
Message::Progress { n_total, n_done } => f
|
2020-06-11 04:04:09 -05:00
|
|
|
.debug_struct("Progress")
|
2020-06-24 09:58:49 -05:00
|
|
|
.field("n_total", n_total)
|
|
|
|
.field("n_done", n_done)
|
2020-06-11 04:04:09 -05:00
|
|
|
.finish(),
|
2020-06-15 06:29:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn handle_is_object_safe() {
|
|
|
|
fn _assert(_: &dyn Handle) {}
|
|
|
|
}
|