use crate::core::error::{Result, XLinkError};
use crate::core::types::{ChannelState, DeviceId, Message, NetworkType};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct BaseChannel<T> {
local_device_id: DeviceId,
channel_type: crate::core::types::ChannelType,
network_type: NetworkType,
peers: Arc<Mutex<HashMap<DeviceId, T>>>,
}
impl<T> BaseChannel<T> {
pub fn new(
local_device_id: DeviceId,
channel_type: crate::core::types::ChannelType,
network_type: NetworkType,
) -> Self {
Self {
local_device_id,
channel_type,
network_type,
peers: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn local_device_id(&self) -> &DeviceId {
&self.local_device_id
}
pub fn channel_type(&self) -> crate::core::types::ChannelType {
self.channel_type
}
pub fn network_type(&self) -> NetworkType {
self.network_type
}
pub fn peers_clone(&self) -> Arc<Mutex<HashMap<DeviceId, T>>> {
Arc::clone(&self.peers)
}
pub async fn add_peer(&self, device_id: DeviceId, info: T) {
let mut peers = self.peers.lock().await;
peers.insert(device_id, info);
}
pub async fn remove_peer(&self, device_id: &DeviceId) {
let mut peers = self.peers.lock().await;
peers.remove(device_id);
}
pub async fn has_peer(&self, device_id: &DeviceId) -> bool {
let peers = self.peers.lock().await;
peers.contains_key(device_id)
}
pub async fn get_peer(&self, device_id: &DeviceId) -> Option<T>
where
T: Clone,
{
let peers = self.peers.lock().await;
peers.get(device_id).cloned()
}
pub async fn send_generic<F1, F2, F3>(
&self,
message: Message,
mut check_connected: F1,
mut send_impl: F2,
get_error_msg: F3,
) -> Result<()>
where
T: Clone,
F1: FnMut(&T) -> bool,
F2: FnMut(&T) -> Result<()>,
F3: FnOnce(&DeviceId) -> String,
{
let peers = self.peers.lock().await;
if let Some(peer_info) = peers.get(&message.recipient) {
let peer_info = peer_info.clone();
drop(peers);
if check_connected(&peer_info) {
send_impl(&peer_info)?;
return Ok(());
}
}
Err(XLinkError::channel_init_failed(
get_error_msg(&message.recipient),
file!(),
))
}
pub async fn check_state_generic<F>(
&self,
target: &DeviceId,
state_builder: F,
) -> Result<ChannelState>
where
T: Clone,
F: FnOnce(&T) -> ChannelState,
{
let peers = self.peers.lock().await;
if let Some(peer_info) = peers.get(target) {
let peer_info = peer_info.clone();
drop(peers);
Ok(state_builder(&peer_info))
} else {
Ok(ChannelState::default())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::types::MessagePayload;
#[tokio::test]
async fn test_base_channel_add_remove_peer() {
let channel = BaseChannel::new(
DeviceId::new(),
crate::core::types::ChannelType::BluetoothLE,
NetworkType::Bluetooth,
);
let device_id = DeviceId::new();
channel.add_peer(device_id, (true, -50)).await;
assert!(channel.has_peer(&device_id).await);
channel.remove_peer(&device_id).await;
assert!(!channel.has_peer(&device_id).await);
}
#[tokio::test]
async fn test_base_channel_send_generic() {
let channel = BaseChannel::new(
DeviceId::new(),
crate::core::types::ChannelType::BluetoothLE,
NetworkType::Bluetooth,
);
let device_id = DeviceId::new();
channel.add_peer(device_id, (true, -50)).await;
let message = Message::new(
DeviceId::new(),
device_id,
MessagePayload::Text("test".to_string()),
);
let result = channel
.send_generic(
message,
|peer_info| peer_info.0,
|_peer_info| {
log::info!("Sending message");
Ok(())
},
|recipient| format!("Device {} not connected", recipient),
)
.await;
assert!(result.is_ok());
}
}