#![doc(html_root_url = "https://docs.rs/scankit")]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::path::PathBuf;
use std::time::SystemTime;
mod error;
pub use error::{Error, Result};
#[cfg(feature = "walk")]
mod walk;
#[cfg(feature = "walk")]
pub use walk::{ScanWalkIter, Scanner};
#[cfg(feature = "watch")]
mod watch;
#[cfg(feature = "watch")]
pub use watch::{ScanEvent, ScanStream};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ScanEntry {
pub path: PathBuf,
pub size_bytes: u64,
pub modified: Option<SystemTime>,
pub extension: String,
}
#[derive(Debug, Default, Clone)]
#[non_exhaustive]
pub struct ScanConfig {
pub max_file_size_bytes: Option<u64>,
#[cfg(feature = "walk")]
pub excludes: Vec<globset::Glob>,
pub follow_symlinks: bool,
}
impl ScanConfig {
#[must_use]
pub fn max_file_size_bytes(mut self, bytes: u64) -> Self {
self.max_file_size_bytes = Some(bytes);
self
}
#[must_use]
pub fn follow_symlinks(mut self, follow: bool) -> Self {
self.follow_symlinks = follow;
self
}
#[cfg(feature = "walk")]
pub fn add_exclude(mut self, pattern: &str) -> Result<Self> {
let glob = globset::Glob::new(pattern)
.map_err(|e| Error::InvalidExclude(format!("`{pattern}`: {e}")))?;
self.excludes.push(glob);
Ok(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_has_no_cap_and_no_excludes() {
let cfg = ScanConfig::default();
assert!(cfg.max_file_size_bytes.is_none());
assert!(!cfg.follow_symlinks);
}
#[test]
fn size_cap_builder_chains() {
let cfg = ScanConfig::default().max_file_size_bytes(1024);
assert_eq!(cfg.max_file_size_bytes, Some(1024));
}
#[test]
fn follow_symlinks_builder_chains() {
let cfg = ScanConfig::default().follow_symlinks(true);
assert!(cfg.follow_symlinks);
}
#[cfg(feature = "walk")]
#[test]
fn add_exclude_accepts_valid_glob() {
let cfg = ScanConfig::default()
.add_exclude("**/.git/**")
.expect("valid glob should accept");
assert_eq!(cfg.excludes.len(), 1);
}
#[cfg(feature = "walk")]
#[test]
fn add_exclude_rejects_malformed_glob() {
let result = ScanConfig::default().add_exclude("[unbalanced");
assert!(matches!(result, Err(Error::InvalidExclude(_))));
}
}