use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
pub mod mastodon;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Domain {
pub domain: String,
pub level: Level,
pub notes: Option<String>,
pub reason: Option<String>,
pub source: String,
}
impl Domain {
pub fn key(&self) -> String {
format!("{}:{}", self.domain, self.source,)
}
pub fn domain(&self) -> &str {
self.domain.as_ref()
}
pub fn level(&self) -> &Level {
&self.level
}
pub fn notes(&self) -> Option<&str> {
self.notes.as_deref()
}
pub fn reason(&self) -> Option<&str> {
self.reason.as_deref()
}
pub fn source(&self) -> &str {
self.source.as_ref()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Level {
Low,
Medium,
Severe,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct List {
pub domains: Vec<Domain>,
}
impl From<Vec<Domain>> for List {
fn from(domains: Vec<Domain>) -> Self {
Self { domains }
}
}
impl List {
pub fn empty() -> Self {
Self { domains: vec![] }
}
pub fn merge(&mut self, mut other: List) {
self.domains.append(&mut other.domains);
}
}
#[derive(Debug, Default, Deserialize)]
pub struct FilterOpt {
pub dedup: Option<bool>,
}
impl From<Search> for FilterOpt {
fn from(value: Search) -> Self {
Self { dedup: value.dedup }
}
}
#[derive(Debug, Default, Clone, Deserialize)]
pub struct Search {
pub threshold: Option<usize>,
pub domain: Option<String>,
pub source: Option<String>,
pub level: Option<Level>,
pub dedup: Option<bool>,
}
#[derive(Debug)]
pub struct SearchState {
pub sources: HashMap<String, HashSet<String>>,
}
impl SearchState {
pub fn new(list: &List) -> Self {
let mut count: HashMap<String, HashSet<String>> = HashMap::new();
for d in list.domains.iter() {
if let Some(sources) = count.get_mut(d.domain()) {
sources.insert(d.source().to_string());
} else {
count.insert(
d.domain().to_string(),
HashSet::from([d.source().to_string()]),
);
}
}
Self { sources: count }
}
}
impl Search {
pub fn criteria() -> Vec<fn(&Self, &Domain, &SearchState) -> bool> {
vec![
Self::threshold_filter,
Self::domain_filter,
Self::source_filter,
Self::level_filter,
]
}
fn threshold_filter(&self, domain: &Domain, state: &SearchState) -> bool {
if let Some(threshold_filter) = self.threshold {
let n: usize = state.sources.get(domain.domain()).map_or(0, |s| s.len());
n >= threshold_filter
} else {
true
}
}
fn domain_filter(&self, domain: &Domain, _: &SearchState) -> bool {
if let Some(domain_filter) = &self.domain {
domain.domain.contains(domain_filter)
} else {
true
}
}
fn source_filter(&self, domain: &Domain, _: &SearchState) -> bool {
if let Some(source_filter) = &self.source {
domain.source.contains(source_filter)
} else {
true
}
}
fn level_filter(&self, domain: &Domain, _: &SearchState) -> bool {
if let Some(level_filter) = &self.level {
domain.level().eq(level_filter)
} else {
true
}
}
}