at protocol indexer with flexible filtering, xrpc queries, and a cursor-backed event stream, built on fjall
at-protocol atproto indexer rust fjall
at main 66 lines 1.8 kB view raw
1use jacquard_common::types::nsid::Nsid; 2use serde::{Deserialize, Serialize}; 3use std::sync::Arc; 4 5pub type FilterHandle = Arc<arc_swap::ArcSwap<FilterConfig>>; 6 7pub fn new_handle(config: FilterConfig) -> FilterHandle { 8 Arc::new(arc_swap::ArcSwap::new(Arc::new(config))) 9} 10 11/// apply a bool patch or set replacement for a single set update. 12#[derive(Debug, Clone, Serialize, Deserialize)] 13#[serde(untagged)] 14pub enum SetUpdate { 15 /// replace the entire set with this list 16 Set(Vec<String>), 17 /// patch: true = add, false = remove 18 Patch(std::collections::HashMap<String, bool>), 19} 20 21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 22#[serde(rename_all = "snake_case")] 23pub enum FilterMode { 24 Filter = 0, 25 Full = 2, 26} 27 28#[derive(Debug, Clone, Serialize)] 29pub struct FilterConfig { 30 pub mode: FilterMode, 31 pub signals: Vec<Nsid<'static>>, 32 pub collections: Vec<Nsid<'static>>, 33} 34 35impl FilterConfig { 36 pub fn new(mode: FilterMode) -> Self { 37 Self { 38 mode, 39 signals: Vec::new(), 40 collections: Vec::new(), 41 } 42 } 43 44 pub fn matches_collection(&self, collection: &str) -> bool { 45 if self.collections.is_empty() { 46 return true; 47 } 48 self.collections.iter().any(|p| nsid_matches(p, collection)) 49 } 50 51 pub fn matches_signal(&self, collection: &str) -> bool { 52 self.signals.iter().any(|p| nsid_matches(p, collection)) 53 } 54 55 pub fn check_signals(&self) -> bool { 56 self.mode == FilterMode::Filter && !self.signals.is_empty() 57 } 58} 59 60fn nsid_matches(pattern: &str, collection: &str) -> bool { 61 if let Some(prefix) = pattern.strip_suffix(".*") { 62 collection == prefix || collection.starts_with(prefix) 63 } else { 64 collection == pattern 65 } 66}