-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
181 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
use std::path::PathBuf; | ||
|
||
use fs_index::watch_index; | ||
|
||
use crate::{AppError, ResourceId}; | ||
|
||
#[derive(Clone, Debug, clap::Args)] | ||
#[clap( | ||
name = "watch", | ||
about = "Watch the ark managed folder for changes and update the index accordingly" | ||
)] | ||
pub struct Watch { | ||
#[clap( | ||
help = "Path to the directory to watch for changes", | ||
default_value = ".", | ||
value_parser | ||
)] | ||
path: PathBuf, | ||
} | ||
|
||
impl Watch { | ||
pub async fn run(&self) -> Result<(), AppError> { | ||
watch_index::<_, ResourceId>(&self.path) | ||
.await | ||
.map_err(|err| AppError::IndexError(err.to_string())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use std::{path::Path, thread}; | ||
|
||
use anyhow::Result; | ||
use log::LevelFilter; | ||
|
||
use dev_hash::Blake3; | ||
use fs_index::watch_index; | ||
|
||
/// Example demonstrating how to use fs_index to watch a directory for changes | ||
/// in a separate thread. This automatically updates the index when changes are | ||
/// detected. | ||
fn main() -> Result<()> { | ||
env_logger::builder() | ||
.filter_level(LevelFilter::Debug) | ||
.init(); | ||
|
||
// Change this to the path of the directory you want to watch | ||
let root = Path::new("test-assets"); | ||
|
||
let thread_handle = thread::spawn(move || { | ||
tokio::runtime::Runtime::new() | ||
.unwrap() | ||
.block_on(async move { | ||
if let Err(err) = watch_index::<_, Blake3>(root).await { | ||
eprintln!("Error in watching index: {:?}", err); | ||
} | ||
}); | ||
}); | ||
|
||
thread_handle | ||
.join() | ||
.expect("Failed to join thread"); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,11 @@ | ||
mod index; | ||
mod serde; | ||
mod utils; | ||
|
||
pub use utils::load_or_build_index; | ||
mod watch; | ||
|
||
pub use index::ResourceIndex; | ||
pub use utils::load_or_build_index; | ||
pub use watch::watch_index; | ||
|
||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
use std::{fs, path::Path}; | ||
|
||
use anyhow::Result; | ||
use futures::{ | ||
channel::mpsc::{channel, Receiver}, | ||
SinkExt, StreamExt, | ||
}; | ||
use log::info; | ||
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; | ||
|
||
use data_resource::ResourceId; | ||
use fs_storage::ARK_FOLDER; | ||
|
||
use crate::ResourceIndex; | ||
|
||
/// Watches a given directory for file system changes and automatically updates | ||
/// the resource index. | ||
/// | ||
/// This function continuously monitors the specified directory and responds to | ||
/// file system events such as file creation, modification, and deletion. When | ||
/// an event is detected, the function updates the associated resource index and | ||
/// stores the changes. | ||
/// | ||
/// The function runs asynchronously, whcih makes it suitable for non-blocking | ||
/// contexts. It uses a recursive watcher to track all changes within the | ||
/// directory tree. Events related to the internal `.ark` folder are ignored to | ||
/// prevent unnecessary updates. | ||
/// | ||
/// # Arguments | ||
/// | ||
/// * `root_path` - The root directory to be watched. This path is canonicalized | ||
/// to handle symbolic links and relative paths correctly. | ||
pub async fn watch_index<P: AsRef<Path>, Id: ResourceId>( | ||
root_path: P, | ||
) -> Result<()> { | ||
log::debug!( | ||
"Attempting to watch index at root path: {:?}", | ||
root_path.as_ref() | ||
); | ||
|
||
let root_path = fs::canonicalize(root_path.as_ref())?; | ||
let mut index: ResourceIndex<Id> = ResourceIndex::build(&root_path)?; | ||
index.store()?; | ||
|
||
let (mut watcher, mut rx) = async_watcher()?; | ||
info!("Watching directory: {:?}", root_path); | ||
let config = Config::default(); | ||
watcher.configure(config)?; | ||
watcher.watch(root_path.as_ref(), RecursiveMode::Recursive)?; | ||
info!("Started watcher with config: \n\t{:?}", config); | ||
|
||
let ark_folder = root_path.join(ARK_FOLDER); | ||
while let Some(res) = rx.next().await { | ||
match res { | ||
Ok(event) => { | ||
// If the event is a change in .ark folder, ignore it | ||
if event | ||
.paths | ||
.iter() | ||
.any(|p| p.starts_with(&ark_folder)) | ||
{ | ||
continue; | ||
} | ||
// If the event is not a create, modify or remove, ignore it | ||
if !(event.kind.is_create() | ||
|| event.kind.is_modify() | ||
|| event.kind.is_remove()) | ||
{ | ||
continue; | ||
} | ||
|
||
info!("Detected event: {:?}", event); | ||
let file = event | ||
.paths | ||
.first() | ||
.expect("Failed to get file path from event"); | ||
log::debug!("Updating index for file: {:?}", file); | ||
let relative_path = file.strip_prefix(&root_path)?; | ||
index.update_one(relative_path)?; | ||
|
||
index.store()?; | ||
info!("Index updated and stored"); | ||
} | ||
Err(e) => log::error!("Error in watcher: {:?}", e), | ||
} | ||
} | ||
|
||
unreachable!("Watcher stream ended unexpectedly"); | ||
} | ||
|
||
fn async_watcher( | ||
) -> notify::Result<(RecommendedWatcher, Receiver<notify::Result<Event>>)> { | ||
let (mut tx, rx) = channel(1); | ||
|
||
let watcher = RecommendedWatcher::new( | ||
move |res| { | ||
futures::executor::block_on(async { | ||
if let Err(err) = tx.send(res).await { | ||
log::error!("Error sending event: {:?}", err); | ||
} | ||
}) | ||
}, | ||
Config::default(), | ||
)?; | ||
|
||
Ok((watcher, rx)) | ||
} |