···
use async_trait::async_trait;
6
+
use tokio::io::{self, AsyncReadExt};
9
+
use futures::prelude::*;
use crate::pattern::Pattern;
#[typetag::deserialize(tag = "type")]
11
-
pub trait Filter: std::fmt::Debug {
15
+
pub trait Filter: std::fmt::Debug + Sync {
async fn matches(&self, path: &Path) -> bool;
15
-
#[derive(Debug, Deserialize)]
16
-
pub struct Name(Pattern);
19
+
macro_rules! filter {
23
+
struct $sname:ident {$($(#[$attr:meta])* $fname:ident : $ftype:ty,)*};
24
+
($self_:ident, $path:ident) -> $body:block
26
+
#[derive(Debug, serde::Deserialize)]
28
+
pub struct $sname { $($(#[$attr])* $fname: $ftype),* }
18
-
#[typetag::deserialize(name = "name")]
20
-
impl Filter for Name {
21
-
async fn matches(&self, path: &Path) -> bool {
22
-
self.0.matches(path)
30
+
#[typetag::deserialize(name = $name)]
32
+
impl Filter for $sname {
33
+
async fn matches(&$self_, $path: &Path) -> bool {
42
+
struct $sname:ident ($($field:ty),*);
43
+
($self_:ident, $path:ident) -> $body:block
45
+
#[derive(Debug, serde::Deserialize)]
47
+
pub struct $sname ($($field),*);
49
+
#[typetag::deserialize(name = $name)]
51
+
impl Filter for $sname {
52
+
async fn matches(&$self_, $path: &Path) -> bool {
26
-
#[derive(Debug, Deserialize)]
29
-
#[serde(with = "Ordering")]
30
-
ordering: std::cmp::Ordering,
61
+
struct Name(Pattern);
63
+
self.0.matches(path)
#[serde(remote = "std::cmp::Ordering")]
69
+
#[serde(rename_all = "snake_case")]
41
-
#[typetag::deserialize(name = "size")]
43
-
impl Filter for Size {
44
-
async fn matches(&self, path: &Path) -> bool {
80
+
#[serde(with = "Ordering")]
81
+
ordering: std::cmp::Ordering,
with_metadata(path, |metadata| {
let len = metadata.len();
48
-
self.size.cmp(&len) == self.ordering
87
+
len.cmp(&self.size) == self.ordering
#[derive(Debug, Deserialize)]
94
+
#[serde(tag = "is", rename_all = "snake_case")]
···
fs::metadata(path).await.map(fun).unwrap_or(false)
129
+
struct Any { filters: Box<[Box<dyn Filter>]>, };
131
+
stream::iter(&*self.filters)
132
+
.any(|f| f.matches(path))
139
+
struct All { filters: Box<[Box<dyn Filter>]>, };
141
+
stream::iter(&*self.filters)
142
+
.all(|f| f.matches(path))
#[derive(Debug, Deserialize)]
148
+
#[serde(rename_all = "snake_case")]
154
+
async fn read_first_bytes(n: usize, path: &Path) -> io::Result<Box<[u8]>> {
155
+
let mut file = fs::File::open(path).await?;
157
+
let mut buf = vec![0; n];
159
+
file.read_exact(&mut buf).await?;
164
+
#[typetag::deserialize(name = "content_type")]
166
+
impl Filter for Magic {
167
+
async fn matches(&self, file: &Path) -> bool {
169
+
Magic::Bytes(ref bytes) => {
170
+
read_first_bytes(bytes.len(), file).await.map(|read| read == *bytes).unwrap_or(false)
172
+
Magic::Mime(_) => unimplemented!()