mod notify_monitor;
pub use notify_monitor::NotifyMonitor;
use crate::{AbsPath, AbsPathBuf};
use std::fmt;
#[derive(Debug, Clone)]
pub enum MonitorEntry {
Files(Vec<AbsPathBuf>),
Directories(MonitorDirectories),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MonitorDirectories {
pub extensions: Vec<String>,
pub include: Vec<AbsPathBuf>,
pub exclude: Vec<AbsPathBuf>,
}
#[derive(Debug, Clone)]
pub struct MonitorConfig {
pub load: Vec<MonitorEntry>,
pub watch: Vec<usize>,
}
pub enum MonitorMessage {
Progress { total: usize, done: usize },
Loaded {
files: Vec<(AbsPathBuf, Option<Vec<u8>>)>,
},
}
pub type Sender = Box<dyn Fn(MonitorMessage) + Send>;
pub trait Monitor {
fn new(sender: Sender) -> Self
where
Self: Sized;
fn set_config(&mut self, config: MonitorConfig);
fn reload(&mut self, path: &AbsPath);
}
impl MonitorDirectories {
pub fn contains_file(&self, path: impl AsRef<AbsPath>) -> bool {
let ext = path.as_ref().extension().unwrap_or_default();
if !self
.extensions
.iter()
.any(|include_ext| include_ext.as_str() == ext)
{
false
} else {
self.includes_path(path)
}
}
pub fn contains_dir(&self, path: impl AsRef<AbsPath>) -> bool {
self.includes_path(path)
}
fn includes_path(&self, path: impl AsRef<AbsPath>) -> bool {
let path = path.as_ref();
let mut include: Option<&AbsPathBuf> = None;
for incl in &self.include {
if path.starts_with(incl) {
include = Some(match include {
Some(prev) if prev.starts_with(incl) => prev,
_ => incl,
})
}
}
let include = match include {
Some(incl) => incl,
None => return false,
};
for excl in &self.exclude {
if path.starts_with(excl) && excl.starts_with(include) {
return false;
}
}
true
}
}
impl MonitorEntry {
pub fn contains_file(&self, path: impl AsRef<AbsPath>) -> bool {
match self {
MonitorEntry::Files(files) => {
let path = path.as_ref();
files.iter().any(|entry| entry == path)
}
MonitorEntry::Directories(dirs) => dirs.contains_file(path),
}
}
pub fn contains_dir(&self, path: impl AsRef<AbsPath>) -> bool {
match self {
MonitorEntry::Files(_) => false,
MonitorEntry::Directories(dirs) => dirs.contains_dir(path),
}
}
}
impl fmt::Debug for MonitorMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MonitorMessage::Loaded { files } => f
.debug_struct("Loaded")
.field("files", &files.len())
.finish(),
MonitorMessage::Progress { total, done } => f
.debug_struct("Progress")
.field("total", total)
.field("done", done)
.finish(),
}
}
}
#[cfg(test)]
mod tests {
use super::{AbsPathBuf, Monitor, MonitorDirectories};
use std::convert::TryInto;
use std::path::PathBuf;
#[test]
fn monitor_is_object_safe() {
fn _assert(_: &dyn Monitor) {}
}
#[test]
fn test_config() {
let abs_manifest_dir: AbsPathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.try_into()
.unwrap();
let config = MonitorDirectories {
extensions: vec!["mun".to_owned()],
include: vec![
abs_manifest_dir.join("src"),
abs_manifest_dir.join("src/.git/special_case"),
],
exclude: vec![
abs_manifest_dir.join(".git"),
abs_manifest_dir.join("src/.git"),
],
};
assert!(!config.contains_file(abs_manifest_dir.join("mod.mun")));
assert!(config.contains_file(abs_manifest_dir.join("src/mod.mun")));
assert!(!config.contains_file(abs_manifest_dir.join("src/mod.rs")));
assert!(!config.contains_file(abs_manifest_dir.join(".git/src/mod.mun")));
assert!(!config.contains_file(abs_manifest_dir.join("src/.git/mod.mun")));
assert!(config.contains_file(abs_manifest_dir.join("src/.git/special_case/mod.mun")));
assert!(config.contains_dir(abs_manifest_dir.join("src")));
}
}