pub mod local;
pub use local::LocalBackend;
use async_trait::async_trait;
use tokio::sync::mpsc;
use crate::error::Result;
use crate::types::{SourceMetadata, SourcePath};
#[derive(Debug, Clone)]
pub enum SourceEvent {
Created { path: SourcePath },
Modified { path: SourcePath },
Deleted { path: SourcePath },
Renamed { from: SourcePath, to: SourcePath },
RescanNeeded,
}
pub struct WatchHandle {
cancel: tokio_util::sync::CancellationToken,
}
impl WatchHandle {
pub fn new(cancel: tokio_util::sync::CancellationToken) -> Self {
Self { cancel }
}
pub fn cancel(&self) {
self.cancel.cancel();
}
pub fn cancel_token(&self) -> tokio_util::sync::CancellationToken {
self.cancel.child_token()
}
}
impl Drop for WatchHandle {
fn drop(&mut self) {
self.cancel.cancel();
}
}
#[async_trait]
pub trait Backend: Send + Sync {
async fn scan(&self, root: &SourcePath) -> Result<Vec<(SourcePath, SourceMetadata)>>;
async fn metadata(&self, path: &SourcePath) -> Result<SourceMetadata>;
async fn read_bytes(&self, path: &SourcePath) -> Result<Vec<u8>>;
async fn watch(
&self,
roots: &[SourcePath],
tx: mpsc::Sender<SourceEvent>,
) -> Result<WatchHandle>;
fn name(&self) -> &str;
fn supports_reflink(&self) -> bool;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_local_backend_implements_trait() {
use std::sync::Arc;
let backend: Arc<dyn Backend> = Arc::new(LocalBackend);
assert_eq!(backend.name(), "local");
assert!(backend.supports_reflink());
}
#[test]
fn test_watch_handle_cancels_on_drop() {
let token = tokio_util::sync::CancellationToken::new();
let child = token.child_token();
let probe = child.clone();
{
let handle = WatchHandle::new(child);
assert!(!token.is_cancelled());
drop(handle);
}
assert!(probe.is_cancelled());
}
}