use std::collections::BTreeMap;
use std::time::Duration;
use crate::adapter::net::behavior::{
predicate::Predicate, tag::Tag, tag::TagKey, tag::TaxonomyAxis,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EdgeKind {
ForkOfParent,
}
impl EdgeKind {
pub fn prefix(&self) -> &'static str {
match self {
EdgeKind::ForkOfParent => "fork-of:",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Distance(pub Duration);
pub trait Aggregator {
type Output;
fn observe(
&mut self,
node_id: u64,
caps: &crate::adapter::net::behavior::capability::CapabilitySet,
);
fn finalize(self) -> Self::Output;
}
#[derive(Default)]
pub struct Count(usize);
impl Aggregator for Count {
type Output = usize;
fn observe(&mut self, _: u64, _: &crate::adapter::net::behavior::capability::CapabilitySet) {
self.0 += 1;
}
fn finalize(self) -> usize {
self.0
}
}
pub struct UniqueAxisValues {
axis: TaxonomyAxis,
key: String,
seen: std::collections::BTreeSet<String>,
}
impl UniqueAxisValues {
pub fn new(axis: TaxonomyAxis, key: impl Into<String>) -> Self {
Self {
axis,
key: key.into(),
seen: std::collections::BTreeSet::new(),
}
}
}
impl Aggregator for UniqueAxisValues {
type Output = Vec<String>;
fn observe(&mut self, _: u64, caps: &crate::adapter::net::behavior::capability::CapabilitySet) {
for tag in &caps.tags {
if let Tag::AxisValue {
axis, key, value, ..
} = tag
{
if *axis == self.axis && key == &self.key {
self.seen.insert(value.clone());
}
}
}
}
fn finalize(self) -> Vec<String> {
self.seen.into_iter().collect()
}
}
pub struct MaxNumericMetadata {
key: String,
current_max: Option<f64>,
}
impl MaxNumericMetadata {
pub fn new(key: impl Into<String>) -> Self {
Self {
key: key.into(),
current_max: None,
}
}
}
impl Aggregator for MaxNumericMetadata {
type Output = Option<f64>;
fn observe(&mut self, _: u64, caps: &crate::adapter::net::behavior::capability::CapabilitySet) {
let Some(raw) = caps.metadata.get(&self.key) else {
return;
};
let Ok(parsed) = raw.parse::<f64>() else {
return;
};
if !parsed.is_finite() {
return;
}
self.current_max = Some(match self.current_max {
Some(m) if m >= parsed => m,
_ => parsed,
});
}
fn finalize(self) -> Option<f64> {
self.current_max
}
}
pub trait CapabilityQuery {
fn filter(
&self,
predicate: &Predicate,
) -> Vec<(
u64,
crate::adapter::net::behavior::capability::CapabilitySet,
)>;
fn match_axis(
&self,
axis: TaxonomyAxis,
key: &str,
value: Option<&str>,
) -> Vec<(
u64,
crate::adapter::net::behavior::capability::CapabilitySet,
)>;
fn aggregate<A>(&self, predicate: &Predicate, agg: A) -> A::Output
where
A: Aggregator;
fn traverse(
&self,
start_node: u64,
start_tag: &Tag,
edge: EdgeKind,
max_depth: u32,
) -> Vec<(u64, Tag)>;
fn nearest<F: Fn(u64) -> Option<Duration>>(
&self,
predicate: &Predicate,
rtt_lookup: F,
n: usize,
) -> Vec<(
u64,
crate::adapter::net::behavior::capability::CapabilitySet,
Option<Distance>,
)>;
}
#[allow(dead_code)]
fn axis_match(
caps: &crate::adapter::net::behavior::capability::CapabilitySet,
axis: TaxonomyAxis,
key: &str,
value: Option<&str>,
) -> bool {
for tag in &caps.tags {
match tag {
Tag::AxisPresent {
axis: tag_axis,
key: tag_key,
} if *tag_axis == axis && tag_key == key && value.is_none() => {
return true;
}
Tag::AxisValue {
axis: tag_axis,
key: tag_key,
value: tag_value,
..
} => {
if *tag_axis != axis || tag_key != key {
continue;
}
match value {
None => return true, Some(target) if tag_value == target => return true,
_ => {}
}
}
_ => {}
}
}
false
}
#[allow(dead_code)]
const _DOC_LINK: BTreeMap<String, String> = BTreeMap::new();
#[allow(dead_code)]
fn _doc_link_tag_key(axis: TaxonomyAxis, k: &str) -> TagKey {
TagKey::new(axis, k.to_string())
}