···
use async_trait::async_trait;
+
use tokio::io::{self, AsyncReadExt};
+
use futures::prelude::*;
use crate::pattern::Pattern;
#[typetag::deserialize(tag = "type")]
+
pub trait Filter: std::fmt::Debug + Sync {
async fn matches(&self, path: &Path) -> bool;
+
struct $sname:ident {$($(#[$attr:meta])* $fname:ident : $ftype:ty,)*};
+
($self_:ident, $path:ident) -> $body:block
+
#[derive(Debug, serde::Deserialize)]
+
pub struct $sname { $($(#[$attr])* $fname: $ftype),* }
+
#[typetag::deserialize(name = $name)]
+
impl Filter for $sname {
+
async fn matches(&$self_, $path: &Path) -> bool {
+
struct $sname:ident ($($field:ty),*);
+
($self_:ident, $path:ident) -> $body:block
+
#[derive(Debug, serde::Deserialize)]
+
pub struct $sname ($($field),*);
+
#[typetag::deserialize(name = $name)]
+
impl Filter for $sname {
+
async fn matches(&$self_, $path: &Path) -> bool {
#[serde(remote = "std::cmp::Ordering")]
+
#[serde(rename_all = "snake_case")]
+
#[serde(with = "Ordering")]
+
ordering: std::cmp::Ordering,
with_metadata(path, |metadata| {
let len = metadata.len();
+
len.cmp(&self.size) == self.ordering
#[derive(Debug, Deserialize)]
+
#[serde(tag = "is", rename_all = "snake_case")]
···
fs::metadata(path).await.map(fun).unwrap_or(false)
+
struct Any { filters: Box<[Box<dyn Filter>]>, };
+
stream::iter(&*self.filters)
+
.any(|f| f.matches(path))
+
struct All { filters: Box<[Box<dyn Filter>]>, };
+
stream::iter(&*self.filters)
+
.all(|f| f.matches(path))
#[derive(Debug, Deserialize)]
+
#[serde(rename_all = "snake_case")]
+
async fn read_first_bytes(n: usize, path: &Path) -> io::Result<Box<[u8]>> {
+
let mut file = fs::File::open(path).await?;
+
let mut buf = vec![0; n];
+
file.read_exact(&mut buf).await?;
+
#[typetag::deserialize(name = "content_type")]
+
impl Filter for Magic {
+
async fn matches(&self, file: &Path) -> bool {
+
Magic::Bytes(ref bytes) => {
+
read_first_bytes(bytes.len(), file).await.map(|read| read == *bytes).unwrap_or(false)
+
Magic::Mime(_) => unimplemented!()