diff --git a/src/storage.rs b/src/storage.rs index c1e5686..984eae6 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -90,6 +90,86 @@ pub mod source { Ok(self.quotes.clone()) } } + + /// Uses a [QuoteFilter] to source quotes from the local filesystem + /// at the beginning of each cycle. + #[derive(Actor)] + pub struct FsFilterSourceManager { + filter: QuoteFilter, + } + + impl FsFilterSourceManager { + pub fn new(filter: QuoteFilter) -> Self { + Self { filter } + } + } + + impl Default for FsFilterSourceManager { + fn default() -> Self { + Self { + filter: QuoteFilter { + content: r".*".to_string(), + // TODO: Maybe make this a compile-time constant for debugging + path: "test/**/*.txt".to_string(), + _dates: vec![], + }, + } + } + } + + impl Message for FsFilterSourceManager { + type Reply = SourceReply; + + async fn handle( + &mut self, + _msg: SourceQuotes, + _ctx: &mut Context, + ) -> Self::Reply { + self.filter.read_files() + } + } + + #[derive(Clone, Debug)] + pub struct QuoteFilter { + path: String, + content: String, + _dates: Vec, + } + + impl QuoteFilter { + // TODO: actually leverage async I/O + fn read_files(&self) -> Result, ()> { + use glob::glob; + use grep::{regex, searcher::sinks}; + + let matcher = regex::RegexMatcher::new(&self.content).map_err(|_| ())?; + let mut searcher = grep::searcher::Searcher::new(); + let mut results = Vec::new(); + + for file in glob(&self.path).map_err(|_| ())? { + let file = match file { + Ok(file) => file, + Err(_) => continue, + }; + + let mut matched = false; + let sink = sinks::Lossy(|_lnum, _line| { + matched = true; + Ok(false) + }); + + let search_result = searcher.search_path(&matcher, &file, sink); + if !matched || search_result.is_err() { + continue; + } + + let contents = std::fs::read_to_string(file).map_err(|_| ())?; + results.push(contents.trim().into()); + } + + Ok(results) + } + } } pub mod queue {