#[serde_with::serde_as]
#[derive(Debug,serde::Deserialize)]
#[serde(deny_unknown_fields)]
#[serde(rename_all="kebab-case")]
pub struct Match {
pub any: Option<Vec<Match>>,
pub categories: Option<std::collections::HashSet<String>>,
pub is_private: Option<bool>,
#[serde_as(as="Option<crate::Cmp<crate::SerdeDuration>>")] pub last_activity: Option<crate::Cmp<std::time::Duration>>,
pub leech_count: Option<crate::Cmp<u64>>,
#[serde_as(as="Option<serde_with::FromInto<crate::SerdeRegexSet>>")] pub name: Option<regex::RegexSet>,
#[serde(default)] pub none: Vec<Match>,
pub ratio: Option<crate::Cmp<f32>>,
#[serde_as(as="Option<crate::Cmp<crate::SerdeDuration>>")] pub seed_time: Option<crate::Cmp<std::time::Duration>>,
pub seed_count: Option<crate::Cmp<u64>>,
pub state: Option<std::collections::HashSet<crate::TorrentState>>,
#[serde_as(as="Option<serde_with::FromInto<crate::SerdeRegexSet>>")] pub tracker_msg: Option<regex::RegexSet>,
#[serde_as(as="Option<serde_with::FromInto<crate::SerdeRegexSet>>")] pub tracker_url: Option<regex::RegexSet>,
}
impl Match {
pub fn match_fast(
&self,
now: std::time::SystemTime,
t: &crate::Torrent,
) -> Option<bool> {
let mut need_slow = false;
if let Some(is_private) = self.is_private {
if t.private != Some(is_private) {
return Some(false)
}
}
if let Some(last_activity) = &self.last_activity {
let since = now.duration_since(t.last_activity)
.unwrap_or_default();
if !last_activity.matches(&since) {
return Some(false)
}
}
if let Some(ratio) = &self.ratio {
if !ratio.matches(&t.ratio) {
return Some(false)
}
}
if let Some(seed_time) = &self.seed_time {
if !seed_time.matches(&t.seeding_time) {
return Some(false)
}
}
if let Some(leech_count) = &self.leech_count {
if !leech_count.matches(&t.leechers) {
return Some(false)
}
}
if let Some(seeder_count) = &self.seed_count {
if !seeder_count.matches(&t.seeders) {
return Some(false)
}
}
if let Some(state) = &self.state {
if !state.contains(&t.state) {
return Some(false)
}
}
if let Some(categories) = &self.categories {
if !categories.contains(&t.category) {
return Some(false)
}
}
if let Some(name) = &self.name {
if !name.is_match(&t.name) {
return Some(false)
}
}
for pred in &self.none {
match pred.match_fast(now, t) {
Some(true) => return Some(false),
Some(false) => continue,
None => need_slow = true,
}
}
if let Some(any) = &self.any {
let mut r = Some(false);
for a in any {
match a.match_fast(now, t) {
Some(true) => {
r = Some(true);
break
},
Some(false) => continue,
None => r = None,
}
}
match r {
Some(true) => {}
Some(false) => return Some(false),
None => need_slow = true,
}
}
need_slow |=
self.tracker_msg.is_some()
|| self.tracker_url.is_some();
if need_slow {
None
} else {
Some(true)
}
}
pub fn match_slow(
&self,
t: &crate::Torrent,
trackers: &[crate::Tracker],
) -> bool {
for pred in &self.none {
if pred.match_slow(t, trackers) {
return false
}
}
if let Some(any) = &self.any {
let mut r = false;
for a in any {
if a.match_slow(t, trackers) {
r = true;
break
}
}
if !r {
return false
}
}
if let Some(tracker_msg) = &self.tracker_msg {
let mut r = false;
for tr in trackers {
if tracker_msg.is_match(&tr.msg) {
r = true;
break
}
}
if !r {
return false
}
}
if let Some(tracker_url) = &self.tracker_url {
let mut r = false;
for tr in trackers {
if tracker_url.is_match(&tr.url) {
r = true;
break
}
}
if !r {
return false
}
}
true
}
}