use super::{EventKind, IdHex, PublicKeyHex, Unixtime};
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::ops::Deref;
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct Filter {
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub ids: Vec<IdHex>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub authors: Vec<PublicKeyHex>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
pub kinds: Vec<EventKind>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(rename = "#e")]
#[serde(default)]
pub e: Vec<IdHex>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(rename = "#p")]
#[serde(default)]
pub p: Vec<PublicKeyHex>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub since: Option<Unixtime>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub until: Option<Unixtime>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub limit: Option<usize>,
}
#[derive(Debug, PartialEq, Eq)]
enum PrefixMatch {
Longer,
Shorter,
Equal,
Mismatch,
}
fn prefix_match(s1: &str, s2: &str) -> PrefixMatch {
match s1.len().cmp(&s2.len()) {
Ordering::Equal => {
if s1 == s2 {
PrefixMatch::Equal
} else {
PrefixMatch::Mismatch
}
}
Ordering::Greater => {
if &s1[..s2.len()] == s2 {
PrefixMatch::Shorter
} else {
PrefixMatch::Mismatch
}
}
Ordering::Less => {
if s1 == &s2[..s1.len()] {
PrefixMatch::Longer
} else {
PrefixMatch::Mismatch
}
}
}
}
fn add_substr<T: Deref<Target = String>>(vec: &mut Vec<T>, add: T) {
for (index, existing) in vec.iter().enumerate() {
match prefix_match(existing, &add) {
PrefixMatch::Equal | PrefixMatch::Shorter => return,
PrefixMatch::Longer => {
vec[index] = add;
return;
}
PrefixMatch::Mismatch => {}
}
}
vec.push(add);
}
fn del_substr<T: Deref<Target = String>>(vec: &mut Vec<T>, del: T) {
let mut marked: Vec<usize> = Vec::new();
for (index, existing) in vec.iter().enumerate() {
match prefix_match(existing, &del) {
PrefixMatch::Equal | PrefixMatch::Shorter => marked.push(index),
_ => {}
}
}
for index in marked.iter().rev() {
let _ = vec.swap_remove(*index);
}
}
impl Filter {
pub fn new() -> Filter {
Default::default()
}
pub fn add_id(&mut self, id_hex: &IdHex, prefix_length: Option<usize>) {
let new_id = if let Some(mut len) = prefix_length {
if len > 64 {
len = 64
}
IdHex(id_hex[..len].to_owned())
} else {
id_hex.to_owned()
};
add_substr(&mut self.ids, new_id);
}
pub fn del_id(&mut self, id_hex: &IdHex, prefix_length: Option<usize>) {
let to_remove = if let Some(mut len) = prefix_length {
if len > 64 {
len = 64
}
IdHex(id_hex[..len].to_owned())
} else {
id_hex.to_owned()
};
del_substr(&mut self.ids, to_remove);
}
pub fn add_author(&mut self, public_key_hex: &PublicKeyHex, prefix_length: Option<usize>) {
let new_author = if let Some(mut len) = prefix_length {
if len > 64 {
len = 64
}
PublicKeyHex(public_key_hex[..len].to_owned())
} else {
public_key_hex.to_owned()
};
add_substr(&mut self.authors, new_author);
}
pub fn del_author(&mut self, public_key_hex: &PublicKeyHex, prefix_length: Option<usize>) {
let to_remove = if let Some(mut len) = prefix_length {
if len > 64 {
len = 64
}
PublicKeyHex(public_key_hex[..len].to_owned())
} else {
public_key_hex.to_owned()
};
del_substr(&mut self.authors, to_remove);
}
pub fn add_event_kind(&mut self, event_kind: EventKind) {
if self.kinds.contains(&event_kind) {
return;
}
self.kinds.push(event_kind);
}
pub fn del_event_kind(&mut self, event_kind: EventKind) {
if let Some(position) = self.kinds.iter().position(|&x| x == event_kind) {
let _ = self.kinds.swap_remove(position);
}
}
pub fn add_e_tag_ids(&mut self, id_hex: IdHex) {
if self.e.contains(&id_hex) {
return;
}
self.e.push(id_hex);
}
pub fn del_e_tag_ids(&mut self, id_hex: &IdHex) {
if let Some(position) = self.e.iter().position(|x| x == id_hex) {
let _ = self.e.swap_remove(position);
}
}
pub fn add_p_tag_public_key(&mut self, public_key_hex: PublicKeyHex) {
if self.p.contains(&public_key_hex) {
return;
}
self.p.push(public_key_hex);
}
pub fn del_p_tag_public_key(&mut self, public_key_hex: &PublicKeyHex) {
if let Some(position) = self.p.iter().position(|x| x == public_key_hex) {
let _ = self.p.swap_remove(position);
}
}
#[allow(dead_code)]
pub(crate) fn mock() -> Filter {
Filter {
ids: vec![IdHex("21345".to_string())],
authors: vec![],
kinds: vec![EventKind::TextNote, EventKind::Metadata],
e: vec![IdHex::mock()],
p: vec![PublicKeyHex(
"221115830ced1ca94352002485fcc7a75dcfe30d1b07f5f6fbe9c0407cfa59a1".to_owned(),
)],
since: Some(Unixtime(1668572286)),
until: None,
limit: None,
}
}
}
#[cfg(test)]
mod test {
use super::*;
test_serde! {Filter, test_filters_serde}
#[test]
fn test_mock() {
assert_eq!(
&serde_json::to_string(&Filter::mock()).unwrap(),
r##"{"ids":["21345"],"kinds":[1,0],"#e":["5df64b33303d62afc799bdc36d178c07b2e1f0d824f31b7dc812219440affab6"],"#p":["221115830ced1ca94352002485fcc7a75dcfe30d1b07f5f6fbe9c0407cfa59a1"],"since":1668572286}"##
);
}
#[test]
fn test_prefix_match() {
assert_eq!(prefix_match("1234", "123"), PrefixMatch::Shorter);
assert_eq!(prefix_match("123", "1234"), PrefixMatch::Longer);
assert_eq!(prefix_match("1234", "1234"), PrefixMatch::Equal);
assert_eq!(prefix_match("1244", "123"), PrefixMatch::Mismatch);
assert_eq!(prefix_match("124", "1234"), PrefixMatch::Mismatch);
assert_eq!(prefix_match("1244", "1234"), PrefixMatch::Mismatch);
assert_eq!(prefix_match("1234", "124"), PrefixMatch::Mismatch);
assert_eq!(prefix_match("123", "1244"), PrefixMatch::Mismatch);
assert_eq!(prefix_match("1234", "1244"), PrefixMatch::Mismatch);
}
#[test]
fn test_add_remove_id() {
let mock = IdHex::mock();
let mut filters: Filter = Filter::new();
filters.add_id(&mock, Some(20));
assert_eq!(filters.ids.len(), 1);
filters.add_id(&mock, None); assert_eq!(filters.ids.len(), 1);
filters.del_id(&mock, None);
assert!(filters.ids.is_empty());
let mut filters: Filter = Filter::new();
filters.add_id(&mock, Some(20));
assert_eq!(filters.ids.len(), 1);
filters.del_id(&mock, None); assert_eq!(filters.ids.len(), 1);
filters.del_id(&mock, Some(20)); assert_eq!(filters.ids.len(), 0);
let base_hex =
IdHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".to_string());
let diff_hex =
IdHex("ffffffffffffffffffffffffffffffffffffffffffff00000000000000000000".to_string());
let mut filters: Filter = Filter::new();
filters.add_id(&base_hex, Some(25));
filters.add_id(&diff_hex, Some(25));
filters.del_id(&base_hex, Some(10));
assert_eq!(filters.ids.len(), 0);
filters.add_id(&base_hex, Some(3000));
}
}