#![allow(dead_code)]
use std::any::Any;
use std::sync::Arc;
use dlep_core::{
DataItem, ExtensionId, LinkMetrics, MacAddress, Message, MessageType, RawDataItem, StatusCode,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct SessionId(pub u64);
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Role {
Router,
Modem,
}
impl Role {
pub fn is_router(self) -> bool {
matches!(self, Role::Router)
}
pub fn is_modem(self) -> bool {
matches!(self, Role::Modem)
}
}
pub trait ExtensionCtx {
fn session_id(&self) -> SessionId;
fn is_router_side(&self) -> bool;
fn send_message(&mut self, msg: Message);
fn emit_event(&mut self, ev: Arc<dyn Any + Send + Sync>);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExtHandled {
Handled,
Passthrough,
}
#[derive(Clone, Copy, Debug)]
pub struct SessionStateSnapshot {
pub up: bool,
}
#[derive(Clone, Copy, Debug)]
pub struct DestinationStateSnapshot {
pub up: bool,
pub last_status: StatusCode,
pub metrics: Option<LinkMetrics>,
}
pub trait DlepExtension: Send + Sync + 'static {
fn advertised_ids(&self) -> &[ExtensionId];
fn on_negotiated(&self, remote_ids: &[ExtensionId]) -> bool {
!self.advertised_ids().is_empty()
&& self
.advertised_ids()
.iter()
.all(|id| remote_ids.contains(id))
}
fn on_unknown_data_item(
&self,
in_message: MessageType,
item: &RawDataItem,
ctx: &mut dyn ExtensionCtx,
) -> ExtHandled {
let _ = (in_message, item, ctx);
ExtHandled::Passthrough
}
fn on_unknown_message(
&self,
message_type: MessageType,
items: &[DataItem],
ctx: &mut dyn ExtensionCtx,
) -> ExtHandled {
let _ = (message_type, items, ctx);
ExtHandled::Passthrough
}
fn on_session_state(&self, state: SessionStateSnapshot, ctx: &mut dyn ExtensionCtx) {
let _ = (state, ctx);
}
fn on_destination_state(
&self,
mac: MacAddress,
state: DestinationStateSnapshot,
ctx: &mut dyn ExtensionCtx,
) {
let _ = (mac, state, ctx);
}
}
#[derive(Clone, Default)]
pub struct ExtensionRegistry {
extensions: Vec<Arc<dyn DlepExtension>>,
}
impl ExtensionRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, ext: Arc<dyn DlepExtension>) {
self.extensions.push(ext);
}
pub fn advertised(&self) -> Vec<ExtensionId> {
let mut out = Vec::new();
for e in &self.extensions {
out.extend_from_slice(e.advertised_ids());
}
out.sort();
out.dedup();
out
}
pub fn negotiate(&self, remote: &[ExtensionId]) -> Vec<Arc<dyn DlepExtension>> {
self.extensions
.iter()
.filter(|e| {
!e.advertised_ids().is_empty()
&& e.advertised_ids().iter().all(|id| remote.contains(id))
&& e.on_negotiated(remote)
})
.cloned()
.collect()
}
pub fn iter(&self) -> impl Iterator<Item = &Arc<dyn DlepExtension>> {
self.extensions.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
struct DefaultExtension;
impl DlepExtension for DefaultExtension {
fn advertised_ids(&self) -> &[ExtensionId] {
&[ExtensionId(65000)]
}
}
struct PermissiveExtension;
impl DlepExtension for PermissiveExtension {
fn advertised_ids(&self) -> &[ExtensionId] {
&[ExtensionId(65000), ExtensionId(65001)]
}
fn on_negotiated(&self, _: &[ExtensionId]) -> bool {
true
}
}
#[test]
fn negotiation_requires_mutual_support_even_for_permissive_plugins() {
let mut r = ExtensionRegistry::new();
r.register(Arc::new(DefaultExtension));
r.register(Arc::new(PermissiveExtension));
assert!(r.negotiate(&[]).is_empty());
assert_eq!(r.negotiate(&[ExtensionId(65000)]).len(), 1);
assert_eq!(
r.negotiate(&[ExtensionId(65000), ExtensionId(65001)]).len(),
2
);
}
struct SelectiveExtension {
ids: Vec<ExtensionId>,
accept: bool,
}
impl DlepExtension for SelectiveExtension {
fn advertised_ids(&self) -> &[ExtensionId] {
&self.ids
}
fn on_negotiated(&self, _: &[ExtensionId]) -> bool {
assert!(!self.ids.is_empty(), "unadvertised plugins must stay inert");
self.accept
}
}
#[test]
fn advertisement_is_sorted_unique_and_negotiation_preserves_plugin_order() {
let mut registry = ExtensionRegistry::new();
for ids in [vec![65002, 65000, 65002], vec![65001], vec![65000]] {
registry.register(Arc::new(SelectiveExtension {
ids: ids.into_iter().map(ExtensionId).collect(),
accept: true,
}));
}
let advertised = vec![ExtensionId(65000), ExtensionId(65001), ExtensionId(65002)];
assert_eq!(registry.advertised(), advertised);
let negotiated = registry.negotiate(&advertised);
assert_eq!(negotiated.len(), 3);
for (actual, registered) in negotiated.iter().zip(registry.iter()) {
assert!(Arc::ptr_eq(actual, registered));
}
}
#[test]
fn plugins_can_opt_out_and_plugins_without_ids_never_negotiate() {
let mut registry = ExtensionRegistry::new();
registry.register(Arc::new(SelectiveExtension {
ids: vec![],
accept: true,
}));
registry.register(Arc::new(SelectiveExtension {
ids: vec![ExtensionId(65000)],
accept: false,
}));
assert!(registry.negotiate(&[ExtensionId(65000)]).is_empty());
}
}