Lev
3 years ago
9 changed files with 279 additions and 7 deletions
@ -1 +1,41 @@
|
||||
pub trait Interface {} |
||||
use alloc::string::String; |
||||
use crate::message::MessageBytes; |
||||
use crate::res::IFResult; |
||||
|
||||
|
||||
/// Some data that can be provided to the interface to send the message to a target.
|
||||
///
|
||||
/// For IP this might be `IP:port`.
|
||||
/// Radio interface, for example, may not have the functionality of targeting, but that's fine
|
||||
pub(crate) type TargetingData = String; |
||||
|
||||
/// In an std environment we require that the interface can be send safely between threads
|
||||
#[cfg(not(std))] |
||||
pub trait InterfaceRequirements {} |
||||
#[cfg(std)] |
||||
pub trait InterfaceRequirements = Send + Sync; |
||||
|
||||
|
||||
/// An interface that can be used to
|
||||
pub trait Interface: InterfaceRequirements { |
||||
/// Run one main loop iteration.
|
||||
/// On platforms that support concurrency, these functions will be run simultaneously for all interfaces.
|
||||
/// Most likely, this function will accept messages and save them somewhere internally to give out later in `Interface.receive()`.
|
||||
///
|
||||
/// For systems that don't support concurrency, there can be only one interface in this function waits for a message (to avoid blocking).
|
||||
/// That's why it's necessary to check if it is the case for this interface, and it's done using function `Interface::has_blocking_main()`
|
||||
fn main_loop_iteration(&mut self) -> IFResult<()>; |
||||
/// Check if `main_loop_iteration` stops execution and waits for a message
|
||||
fn has_blocking_main(&self) -> bool { |
||||
false // hopefully...
|
||||
} |
||||
/// Get some way of identification for this interface
|
||||
fn id(&self) -> &str; |
||||
/// Send a message. If no `interface_data` is provided, we should consider it to be a broadcast.
|
||||
/// If, on the other hand, `interface_data` is not `None`, it should be used to send the message to the target.
|
||||
fn send(&mut self, message: &[u8] /*MessageBytes*/, interface_data: Option<TargetingData>) -> IFResult<()>; |
||||
/// Receive a message through this interface. Returns a result with an option of (message bytes, target).
|
||||
/// `None` means there is no message available at the time.
|
||||
/// The implementations of this function shouldn't wait for new messages, but
|
||||
fn receive(&mut self) -> IFResult<Option<(MessageBytes, TargetingData /*interface data*/)>>; |
||||
} |
||||
|
@ -0,0 +1,6 @@
|
||||
use crate::interface::Interface; |
||||
use alloc::vec; |
||||
|
||||
pub fn get_interfaces() -> alloc::vec::Vec<alloc::boxed::Box<dyn Interface>> { |
||||
vec![] |
||||
} |
@ -0,0 +1,5 @@
|
||||
use alloc::vec::Vec; |
||||
|
||||
|
||||
/// A serialized message
|
||||
pub(crate) type MessageBytes = Vec<u8>; |
@ -0,0 +1,19 @@
|
||||
use alloc::format; |
||||
use alloc::string::String; |
||||
use core::fmt::Debug; |
||||
|
||||
/// An enum for all errors that may occur
|
||||
pub enum IFError { |
||||
/// An error that was created in some dependency and then converted to `IFError`
|
||||
General(String), |
||||
} |
||||
|
||||
|
||||
impl<T: Debug> From<T> for IFError { |
||||
/// Convert from other error
|
||||
fn from(e: T) -> Self { |
||||
Self::General(format!("{:?}", e)) |
||||
} |
||||
} |
||||
|
||||
pub type IFResult<T> = Result<T, IFError>; |
@ -1 +1,95 @@
|
||||
use alloc::boxed::Box; |
||||
use alloc::string::String; |
||||
use alloc::vec::Vec; |
||||
use crate::interface::{Interface, TargetingData}; |
||||
use crate::message::MessageBytes; |
||||
use crate::res::IFResult; |
||||
|
||||
#[cfg(std)] |
||||
use rayon::prelude::*; |
||||
|
||||
|
||||
/// An identification of a peer - something that we can use to send a message to id
|
||||
#[derive(Clone, Debug)] |
||||
pub struct PeerInfo { |
||||
/// Something to locally identify this peer
|
||||
pub peer_id: u64, |
||||
/// ID of the interface through which we communicate with this peer
|
||||
pub interface_id: String, |
||||
/// Data that should be passed to the interface to send a message to this peer
|
||||
pub interface_targeting_data: TargetingData, |
||||
} |
||||
|
||||
impl PeerInfo { |
||||
fn new(interface_id: String, interface_targeting_data: TargetingData) -> Self { |
||||
Self { |
||||
peer_id: rand::random(), |
||||
interface_id, |
||||
interface_targeting_data, |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
/// The struct that manages all the communication with peers
|
||||
pub struct Transport { |
||||
pub interfaces: Vec<Box<dyn Interface>>, |
||||
peers: Vec<PeerInfo>, |
||||
} |
||||
|
||||
impl Transport { |
||||
/// Find a peer in `self.peers` by its id
|
||||
fn get_peer_by_id(&self, peer_id: u64) -> Option<PeerInfo> { |
||||
self.peers.iter().find(|peer| peer.peer_id == peer_id).cloned() |
||||
} |
||||
|
||||
/// Try to find a peer in `self.peers` by interface_id and targeting data
|
||||
fn get_peer_by_parameters(&self, interface_id: &str, data: &str /*&TargetingData*/) -> Option<&PeerInfo> { |
||||
self.peers.iter().find(|peer| peer.interface_id == interface_id && peer.interface_targeting_data == data) |
||||
} |
||||
|
||||
/// Insert a new peer into out database and return its id or find an existing one with these parameters
|
||||
fn find_or_add_peer(&mut self, interface_id: String, data: TargetingData) -> u64 { |
||||
match self.get_peer_by_parameters(interface_id.as_str(), &data) { |
||||
None => { |
||||
let new_peer = PeerInfo::new(interface_id, data); |
||||
let peer_id = new_peer.peer_id; |
||||
self.peers.push(new_peer); |
||||
peer_id |
||||
} |
||||
Some(peer) => peer.peer_id |
||||
} |
||||
} |
||||
|
||||
/// Get interface index by its ID
|
||||
fn interface_index_by_id(&self, interface_id: &str) -> usize { |
||||
self.interfaces.iter().position(|interface| interface.id() == interface_id).unwrap_or_else(|| panic!("Invalid interface id")) |
||||
} |
||||
|
||||
/// Send message bytes to a peer if data is provided or broadcast the data if `peer == None`
|
||||
pub fn send_message(&mut self, message: MessageBytes, peer_id: Option<u64>) -> IFResult<()> { |
||||
let peer = if let Some(peer_id) = peer_id { self.get_peer_by_id(peer_id) } else { None }; |
||||
match peer { |
||||
// Broadcast
|
||||
None => { |
||||
#[cfg(not(std))] |
||||
{ |
||||
for interface in &mut self.interfaces { |
||||
interface.send(&message, None)?; |
||||
} |
||||
} |
||||
// If we have concurrency, we will do it concurrently
|
||||
#[cfg(std)] |
||||
{ |
||||
self.interfaces.par_iter_mut().map(|interface| interface.send(&message, None)).for_each(drop); |
||||
} |
||||
Ok(()) |
||||
} |
||||
// Singlecast
|
||||
Some(peer) => { |
||||
let interface_ind = self.interface_index_by_id(peer.interface_id.as_str()); |
||||
self.interfaces[interface_ind].send(&message, Some(peer.interface_targeting_data)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue