ergot_base/interface_manager/mod.rs
1//! The Interface Manager
2//!
3//! The [`NetStack`] is generic over a "Profile", which is how it handles
4//! any external interfaces of the current program or device.
5//!
6//! Different profiles may support a various number of external
7//! interfaces. The simplest profile is the "Null Profile",
8//! Which supports no external interfaces, meaning that messages may only be
9//! routed locally.
10//!
11//! The next simplest profile is one that only supports zero or one
12//! active interfaces, for example if a device is directly connected to a PC
13//! using USB. In this case, routing is again simple: if messages are not
14//! intended for the local device, they should be routed out of the one external
15//! interface. Similarly, if we support an interface, but it is not connected
16//! (e.g. the USB cable is unplugged), all packets with external destinations
17//! will fail to send.
18//!
19//! For more complex devices, a profile with multiple (bounded or
20//! unbounded) interfaces, and more complex routing capabilities, may be
21//! selected.
22//!
23//! Unlike Sockets, which might be various and diverse on all systems, a system
24//! is expected to have one statically-known profile, which may
25//! manage various and diverse interfaces.
26//!
27//! In general when sending a message, the [`NetStack`] will check if the
28//! message is definitively for the local device (e.g. Net ID = 0, Node ID = 0),
29//! and if not the NetStack will pass the message to the Interface Manager. If
30//! the profile can route this packet, it informs the NetStack it has
31//! done so. If the Interface Manager realizes that the packet is still for us
32//! (e.g. matching a Net ID and Node ID of the local device), it may bounce the
33//! message back to the NetStack to locally route.
34//!
35//! [`NetStack`]: crate::NetStack
36
37use crate::{AnyAllAppendix, Header, ProtocolError, wire_frames::CommonHeader};
38use serde::Serialize;
39
40pub mod interface_impls;
41pub mod profiles;
42pub mod utils;
43
44pub trait ConstInit {
45 const INIT: Self;
46}
47
48// An interface send is very similar to a socket send, with the exception
49// that interface sends are ALWAYS a serializing operation (or required
50// serialization has already been done), which means we don't need to
51// differentiate between "send owned" and "send borrowed". The exception
52// to this is "send raw", where serialization has already been done, e.g.
53// if we are routing a packet.
54pub trait Profile {
55 /// The kind of type that is used to identify a single interface.
56 /// If a Profile only supports a single interface, this is often the `()` type.
57 /// If a Profile supports many interfaces, this could be an enum or integer type.
58 type InterfaceIdent;
59
60 fn send<T: Serialize>(&mut self, hdr: &Header, data: &T) -> Result<(), InterfaceSendError>;
61 fn send_err(&mut self, hdr: &Header, err: ProtocolError) -> Result<(), InterfaceSendError>;
62 fn send_raw(
63 &mut self,
64 hdr: &Header,
65 hdr_raw: &[u8],
66 data: &[u8],
67 ) -> Result<(), InterfaceSendError>;
68
69 fn interface_state(&mut self, ident: Self::InterfaceIdent) -> Option<InterfaceState>;
70 fn set_interface_state(
71 &mut self,
72 ident: Self::InterfaceIdent,
73 state: InterfaceState,
74 ) -> Result<(), SetStateError>;
75}
76
77/// Interfaces define how messages are transported over the wire
78pub trait Interface {
79 /// The Sink is the type used to send messages out of the Profile
80 type Sink: InterfaceSink;
81}
82
83/// The "Sink" side of the interface.
84///
85/// This is typically held by a profile, and feeds data to the interface's
86/// TX worker.
87#[allow(clippy::result_unit_err)]
88pub trait InterfaceSink {
89 fn send_ty<T: Serialize>(
90 &mut self,
91 hdr: &CommonHeader,
92 apdx: Option<&AnyAllAppendix>,
93 body: &T,
94 ) -> Result<(), ()>;
95 fn send_raw(&mut self, hdr: &CommonHeader, hdr_raw: &[u8], body: &[u8]) -> Result<(), ()>;
96 fn send_err(&mut self, hdr: &CommonHeader, err: ProtocolError) -> Result<(), ()>;
97}
98
99#[derive(Debug, PartialEq, Eq)]
100#[non_exhaustive]
101pub enum InterfaceSendError {
102 /// Refusing to send local destination remotely
103 DestinationLocal,
104 /// Profile does not know how to route to requested destination
105 NoRouteToDest,
106 /// Profile found a destination interface, but that interface
107 /// was full in space/slots
108 InterfaceFull,
109 /// TODO: Remove
110 PlaceholderOhNo,
111 /// Destination was an "any" port, but a key was not provided
112 AnyPortMissingKey,
113 /// TTL has reached the terminal value
114 TtlExpired,
115}
116
117/// An error when deregistering an interface
118#[derive(Debug, PartialEq, Eq)]
119#[non_exhaustive]
120pub enum DeregisterError {
121 NoSuchInterface,
122}
123
124#[derive(Clone, Copy, Debug, PartialEq)]
125pub enum InterfaceState {
126 // Missing sink, no net id
127 Down,
128 // Has sink, no net id
129 Inactive,
130 // Has sink, has node_id but not net_id
131 ActiveLocal { node_id: u8 },
132 // Has sink, has net id
133 Active { net_id: u16, node_id: u8 },
134}
135
136#[derive(Clone, Copy, Debug, PartialEq)]
137#[non_exhaustive]
138pub enum RegisterSinkError {
139 AlreadyActive,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq)]
143#[non_exhaustive]
144pub enum SetStateError {
145 InterfaceNotFound,
146 InvalidNodeId,
147}
148
149impl InterfaceSendError {
150 pub fn to_error(&self) -> ProtocolError {
151 match self {
152 InterfaceSendError::DestinationLocal => ProtocolError::ISE_DESTINATION_LOCAL,
153 InterfaceSendError::NoRouteToDest => ProtocolError::ISE_NO_ROUTE_TO_DEST,
154 InterfaceSendError::InterfaceFull => ProtocolError::ISE_INTERFACE_FULL,
155 InterfaceSendError::PlaceholderOhNo => ProtocolError::ISE_PLACEHOLDER_OH_NO,
156 InterfaceSendError::AnyPortMissingKey => ProtocolError::ISE_ANY_PORT_MISSING_KEY,
157 InterfaceSendError::TtlExpired => ProtocolError::ISE_TTL_EXPIRED,
158 }
159 }
160}