use crate::config::{
Configuration, Watch,
rt::{BuildOptions, RtcBuild, RtcBuilder},
};
use anyhow::anyhow;
use std::{ops::Deref, path::PathBuf, sync::Arc, time::Duration};
#[derive(Clone, Debug)]
pub struct GlobMatcher {
patterns: Vec<globset::Glob>,
matcher: globset::GlobSet,
}
impl GlobMatcher {
pub fn new() -> Self {
Self {
patterns: Vec::new(),
matcher: globset::GlobSet::empty(),
}
}
pub fn add(&mut self, pattern: globset::Glob) -> Result<(), globset::Error> {
let mut matcher = globset::GlobSet::builder();
for pattern in self.patterns.iter().cloned() {
matcher.add(pattern);
}
matcher.add(pattern.clone());
let matcher = matcher.build()?;
self.patterns.push(pattern);
self.matcher = matcher;
Ok(())
}
pub fn is_match(&self, path: impl AsRef<std::path::Path>) -> bool {
self.matcher.is_match(path.as_ref())
}
}
#[derive(Clone, Debug)]
pub struct RtcWatch {
pub build: Arc<RtcBuild>,
pub paths: Vec<PathBuf>,
pub ignored_paths: GlobMatcher,
pub poll: Option<Duration>,
pub enable_cooldown: bool,
pub clear_screen: bool,
pub no_error_reporting: bool,
}
impl Deref for RtcWatch {
type Target = RtcBuild;
fn deref(&self) -> &Self::Target {
&self.build
}
}
#[derive(Clone, Debug)]
pub struct WatchOptions {
pub build: BuildOptions,
pub poll: Option<Duration>,
pub enable_cooldown: bool,
pub clear_screen: bool,
pub no_error_reporting: bool,
}
impl RtcWatch {
pub(crate) fn new(config: Configuration, opts: WatchOptions) -> anyhow::Result<Self> {
let WatchOptions {
build: build_opts,
poll,
enable_cooldown,
clear_screen,
no_error_reporting,
} = opts;
let Watch { watch, ignore } = config.watch.clone();
let build = RtcBuild::new(config, build_opts)?;
tracing::debug!("Disable error reporting: {no_error_reporting}");
let mut paths = vec![];
for path in watch {
let path = build.working_directory.join(path);
let canon_path = path.canonicalize().map_err(|_| {
anyhow!(
"error taking the canonical path to the watch path: {:?}",
path
)
})?;
paths.push(canon_path);
}
if paths.is_empty() {
paths.push(build.target_parent.clone());
}
let mut ignored_paths = GlobMatcher::new();
let Some(final_dist) = build.final_dist.to_str() else {
return Err(anyhow!("could not convert final distribution path to glob"));
};
let final_dist = globset::Glob::new(final_dist).map_err(|err| anyhow!(err))?;
ignored_paths.add(final_dist).map_err(|err| anyhow!(err))?;
let final_dist_recursive = build.final_dist.join("**");
let Some(final_dist_recursive) = final_dist_recursive.to_str() else {
return Err(anyhow!("could not convert final distribution path to glob"));
};
let final_dist_recursive =
globset::Glob::new(final_dist_recursive).map_err(|err| anyhow!(err))?;
ignored_paths
.add(final_dist_recursive)
.map_err(|err| anyhow!(err))?;
let working_dir = build
.working_directory
.canonicalize()
.map_err(|_| anyhow!("error taking the canonical path to the working directory"))?;
for path in ignore {
let path = working_dir.join(path);
let Some(glob) = path.to_str() else {
return Err(anyhow!("could not convert {:?} to str", path));
};
let glob = globset::Glob::new(glob).map_err(|err| anyhow!(err))?;
ignored_paths.add(glob).map_err(|err| anyhow!(err))?;
}
Ok(Self {
build: Arc::new(build),
paths,
ignored_paths,
poll,
enable_cooldown,
clear_screen,
no_error_reporting,
})
}
}
impl RtcBuilder for RtcWatch {
type Options = WatchOptions;
async fn build(configuration: Configuration, options: Self::Options) -> anyhow::Result<Self> {
Self::new(configuration, options)
}
}