nautilus_persistence/writer/
filter.rs1use ahash::{AHashMap, AHashSet};
19use nautilus_model::{
20 data::NautilusRecordType,
21 instruments::{InstrumentAny, NautilusInstrumentType},
22};
23
24use crate::{catalog::traits::NautilusRecordTypePrefix, common::paths::CatalogPathPrefix};
25
26#[derive(Clone, Debug, Default, PartialEq)]
28pub struct WriterRecordFilter {
29 entries: AHashMap<String, Option<AHashSet<String>>>,
30 instrument_types: AHashSet<String>,
31}
32
33impl WriterRecordFilter {
34 #[must_use]
36 pub fn new() -> Self {
37 Self::default()
38 }
39
40 #[must_use]
42 pub fn from_record_types(record_types: impl IntoIterator<Item = NautilusRecordType>) -> Self {
43 let mut filter = Self::new();
44 for record_type in record_types {
45 filter.insert(&record_type, None);
46 }
47 filter
48 }
49
50 pub fn insert(&mut self, record_type: &NautilusRecordType, identifiers: Option<Vec<String>>) {
52 let prefix = record_type.path_prefix().into_owned();
53 self.insert_prefix(prefix, identifiers);
54 }
55
56 pub fn insert_prefix(&mut self, prefix: impl Into<String>, identifiers: Option<Vec<String>>) {
58 let identifiers = identifiers.map(|values| values.into_iter().collect());
59 self.entries.insert(prefix.into(), identifiers);
60 }
61
62 pub fn insert_instrument_type(&mut self, instrument_type: &NautilusInstrumentType) {
64 self.instrument_types.insert(instrument_type.to_string());
65 }
66
67 #[must_use]
69 pub fn is_empty(&self) -> bool {
70 self.entries.is_empty() && self.instrument_types.is_empty()
71 }
72
73 #[must_use]
75 pub fn contains_prefix(&self, record_prefix: &str) -> bool {
76 self.is_empty()
77 || self.entries.contains_key(record_prefix)
78 || (!self.instrument_types.is_empty() && record_prefix == InstrumentAny::path_prefix())
79 }
80
81 #[must_use]
83 pub fn allows(
84 &self,
85 record_prefix: &str,
86 identifier: Option<&str>,
87 instrument_type: Option<&str>,
88 ) -> bool {
89 if self.is_empty() {
90 return true;
91 }
92
93 let Some(identifiers) = self.entries.get(record_prefix) else {
94 return record_prefix == InstrumentAny::path_prefix()
95 && !self.instrument_types.is_empty()
96 && instrument_type.is_some_and(|value| self.instrument_types.contains(value));
97 };
98
99 if record_prefix == InstrumentAny::path_prefix()
100 && !self.instrument_types.is_empty()
101 && !instrument_type.is_some_and(|value| self.instrument_types.contains(value))
102 {
103 return false;
104 }
105
106 match identifiers {
107 None => true,
108 Some(identifiers) => {
109 identifier.is_some_and(|identifier| identifiers.contains(identifier))
110 }
111 }
112 }
113}