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