1#![allow(dead_code)]
11
12use std::any::Any;
13use std::sync::Arc;
14
15use dlep_core::{
16 DataItem, ExtensionId, LinkMetrics, MacAddress, Message, MessageType, RawDataItem, StatusCode,
17};
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
24pub struct SessionId(pub u64);
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
30pub enum Role {
31 Router,
32 Modem,
33}
34
35impl Role {
36 pub fn is_router(self) -> bool {
37 matches!(self, Role::Router)
38 }
39 pub fn is_modem(self) -> bool {
40 matches!(self, Role::Modem)
41 }
42}
43
44pub trait ExtensionCtx {
47 fn session_id(&self) -> SessionId;
48 fn is_router_side(&self) -> bool;
49 fn send_message(&mut self, msg: Message);
50 fn emit_event(&mut self, ev: Arc<dyn Any + Send + Sync>);
51}
52
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub enum ExtHandled {
55 Handled,
56 Passthrough,
57}
58
59#[derive(Clone, Copy, Debug)]
61pub struct SessionStateSnapshot {
62 pub up: bool,
63}
64
65#[derive(Clone, Copy, Debug)]
66pub struct DestinationStateSnapshot {
67 pub up: bool,
68 pub last_status: StatusCode,
71 pub metrics: Option<LinkMetrics>,
76}
77
78pub trait DlepExtension: Send + Sync + 'static {
81 fn advertised_ids(&self) -> &[ExtensionId];
83
84 fn on_negotiated(&self, remote_ids: &[ExtensionId]) -> bool {
87 !self.advertised_ids().is_empty()
88 && self
89 .advertised_ids()
90 .iter()
91 .all(|id| remote_ids.contains(id))
92 }
93
94 fn on_unknown_data_item(
99 &self,
100 in_message: MessageType,
101 item: &RawDataItem,
102 ctx: &mut dyn ExtensionCtx,
103 ) -> ExtHandled {
104 let _ = (in_message, item, ctx);
105 ExtHandled::Passthrough
106 }
107
108 fn on_unknown_message(
111 &self,
112 message_type: MessageType,
113 items: &[DataItem],
114 ctx: &mut dyn ExtensionCtx,
115 ) -> ExtHandled {
116 let _ = (message_type, items, ctx);
117 ExtHandled::Passthrough
118 }
119
120 fn on_session_state(&self, state: SessionStateSnapshot, ctx: &mut dyn ExtensionCtx) {
121 let _ = (state, ctx);
122 }
123
124 fn on_destination_state(
125 &self,
126 mac: MacAddress,
127 state: DestinationStateSnapshot,
128 ctx: &mut dyn ExtensionCtx,
129 ) {
130 let _ = (mac, state, ctx);
131 }
132}
133
134#[derive(Clone, Default)]
136pub struct ExtensionRegistry {
137 extensions: Vec<Arc<dyn DlepExtension>>,
138}
139
140impl ExtensionRegistry {
141 pub fn new() -> Self {
142 Self::default()
143 }
144
145 pub fn register(&mut self, ext: Arc<dyn DlepExtension>) {
146 self.extensions.push(ext);
147 }
148
149 pub fn advertised(&self) -> Vec<ExtensionId> {
151 let mut out = Vec::new();
152 for e in &self.extensions {
153 out.extend_from_slice(e.advertised_ids());
154 }
155 out.sort();
156 out.dedup();
157 out
158 }
159
160 pub fn negotiate(&self, remote: &[ExtensionId]) -> Vec<Arc<dyn DlepExtension>> {
163 self.extensions
164 .iter()
165 .filter(|e| {
166 !e.advertised_ids().is_empty()
167 && e.advertised_ids().iter().all(|id| remote.contains(id))
168 && e.on_negotiated(remote)
169 })
170 .cloned()
171 .collect()
172 }
173
174 pub fn iter(&self) -> impl Iterator<Item = &Arc<dyn DlepExtension>> {
175 self.extensions.iter()
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 struct DefaultExtension;
183 impl DlepExtension for DefaultExtension {
184 fn advertised_ids(&self) -> &[ExtensionId] {
185 &[ExtensionId(65000)]
186 }
187 }
188 struct PermissiveExtension;
189 impl DlepExtension for PermissiveExtension {
190 fn advertised_ids(&self) -> &[ExtensionId] {
191 &[ExtensionId(65000), ExtensionId(65001)]
192 }
193 fn on_negotiated(&self, _: &[ExtensionId]) -> bool {
194 true
195 }
196 }
197 #[test]
198 fn negotiation_requires_mutual_support_even_for_permissive_plugins() {
199 let mut r = ExtensionRegistry::new();
200 r.register(Arc::new(DefaultExtension));
201 r.register(Arc::new(PermissiveExtension));
202 assert!(r.negotiate(&[]).is_empty());
203 assert_eq!(r.negotiate(&[ExtensionId(65000)]).len(), 1);
204 assert_eq!(
205 r.negotiate(&[ExtensionId(65000), ExtensionId(65001)]).len(),
206 2
207 );
208 }
209 struct SelectiveExtension {
210 ids: Vec<ExtensionId>,
211 accept: bool,
212 }
213 impl DlepExtension for SelectiveExtension {
214 fn advertised_ids(&self) -> &[ExtensionId] {
215 &self.ids
216 }
217 fn on_negotiated(&self, _: &[ExtensionId]) -> bool {
218 assert!(!self.ids.is_empty(), "unadvertised plugins must stay inert");
219 self.accept
220 }
221 }
222
223 #[test]
224 fn advertisement_is_sorted_unique_and_negotiation_preserves_plugin_order() {
225 let mut registry = ExtensionRegistry::new();
226 for ids in [vec![65002, 65000, 65002], vec![65001], vec![65000]] {
227 registry.register(Arc::new(SelectiveExtension {
228 ids: ids.into_iter().map(ExtensionId).collect(),
229 accept: true,
230 }));
231 }
232 let advertised = vec![ExtensionId(65000), ExtensionId(65001), ExtensionId(65002)];
233 assert_eq!(registry.advertised(), advertised);
234 let negotiated = registry.negotiate(&advertised);
235 assert_eq!(negotiated.len(), 3);
236 for (actual, registered) in negotiated.iter().zip(registry.iter()) {
237 assert!(Arc::ptr_eq(actual, registered));
238 }
239 }
240
241 #[test]
242 fn plugins_can_opt_out_and_plugins_without_ids_never_negotiate() {
243 let mut registry = ExtensionRegistry::new();
244 registry.register(Arc::new(SelectiveExtension {
245 ids: vec![],
246 accept: true,
247 }));
248 registry.register(Arc::new(SelectiveExtension {
249 ids: vec![ExtensionId(65000)],
250 accept: false,
251 }));
252 assert!(registry.negotiate(&[ExtensionId(65000)]).is_empty());
253 }
254}