use std::{collections::HashMap, sync::Arc};
use tycho_simulation::tycho_common::models::{protocol::ProtocolComponent, Address};
use crate::{
feed::{events::MarketEvent, market_data::MarketState},
types::ComponentId,
};
#[derive(Clone)]
pub struct ExclusivityPolicy {
is_exclusive: Arc<dyn Fn(&ProtocolComponent) -> bool + Send + Sync>,
}
impl ExclusivityPolicy {
pub fn new<F>(predicate: F) -> Self
where
F: Fn(&ProtocolComponent) -> bool + Send + Sync + 'static,
{
Self { is_exclusive: Arc::new(predicate) }
}
pub fn is_exclusive(&self, component: &ProtocolComponent) -> bool {
(self.is_exclusive)(component)
}
pub(crate) fn filter_topology(
&self,
market: &MarketState,
topology: HashMap<ComponentId, Vec<Address>>,
) -> HashMap<ComponentId, Vec<Address>> {
topology
.into_iter()
.filter(|(id, _)| {
market
.get_component(id)
.is_none_or(|c| !self.is_exclusive(c))
})
.collect()
}
pub(crate) fn scope_event(&self, market: &MarketState, event: MarketEvent) -> MarketEvent {
let MarketEvent::MarketUpdated { added_components, removed_components, updated_components } =
event;
let added_components = self.filter_topology(market, added_components);
let removed_components = self.filter_component_ids(market, &removed_components);
let updated_components = self.filter_component_ids(market, &updated_components);
MarketEvent::MarketUpdated { added_components, removed_components, updated_components }
}
fn filter_component_ids(&self, market: &MarketState, ids: &[ComponentId]) -> Vec<ComponentId> {
ids.iter()
.filter(|id| {
market
.get_component(id)
.is_none_or(|c| !self.is_exclusive(c))
})
.cloned()
.collect()
}
}
impl std::fmt::Debug for ExclusivityPolicy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExclusivityPolicy")
.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
algorithm::test_utils::{component, token},
feed::{events::MarketEvent, market_data::MarketState},
};
fn exclusive_protocol_policy() -> ExclusivityPolicy {
ExclusivityPolicy::new(|c: &ProtocolComponent| c.protocol_system == "vm:exclusive")
}
fn exclusive_component(id: &str) -> ProtocolComponent {
let mut c = component(id, &[token(0x01, "A"), token(0x02, "B")]);
c.protocol_system = "vm:exclusive".to_string();
c
}
fn public_component(id: &str) -> ProtocolComponent {
component(id, &[token(0x01, "A"), token(0x02, "B")])
}
fn market_with(components: Vec<ProtocolComponent>) -> MarketState {
let mut market = MarketState::new();
market.upsert_components(components);
market
}
#[test]
fn test_is_exclusive_reflects_predicate() {
let policy = exclusive_protocol_policy();
assert!(policy.is_exclusive(&exclusive_component("excl-1")));
assert!(!policy.is_exclusive(&public_component("pub-1")));
}
#[test]
fn test_filter_topology_excludes_exclusive() {
let policy = exclusive_protocol_policy();
let market = market_with(vec![public_component("pub-1"), exclusive_component("excl-1")]);
let topology = market.component_topology();
let filtered = policy.filter_topology(&market, topology);
assert!(filtered.contains_key("pub-1"));
assert!(!filtered.contains_key("excl-1"));
}
#[test]
fn test_scope_event_excludes_exclusive() {
let policy = exclusive_protocol_policy();
let market = market_with(vec![public_component("pub-1"), exclusive_component("excl-1")]);
let event = MarketEvent::MarketUpdated {
added_components: HashMap::from([
("pub-1".to_string(), vec![]),
("excl-1".to_string(), vec![]),
]),
removed_components: vec!["pub-1".to_string(), "excl-1".to_string()],
updated_components: vec!["pub-1".to_string(), "excl-1".to_string()],
};
let MarketEvent::MarketUpdated { added_components, removed_components, updated_components } =
policy.scope_event(&market, event);
assert!(added_components.contains_key("pub-1"));
assert!(!added_components.contains_key("excl-1"));
assert_eq!(removed_components, vec!["pub-1".to_string()]);
assert_eq!(updated_components, vec!["pub-1".to_string()]);
}
}