use super::{
Location, Topology, TopologyError, Transport, TransportError, TransportFactory, TransportResult,
};
use crate::identifiers::RoleName;
use crate::runtime::sync::RwLock;
use crate::{read_lock, write_lock};
use std::collections::BTreeMap;
use std::sync::Arc;
use thiserror::Error;
pub struct TopologyHandler {
topology: Topology,
role: RoleName,
transports: Arc<RwLock<BTreeMap<RoleName, Box<dyn Transport>>>>,
initialized: Arc<RwLock<bool>>,
}
impl TopologyHandler {
pub fn new(topology: Topology, role: RoleName) -> Self {
Self {
topology,
role,
transports: Arc::new(RwLock::new(BTreeMap::new())),
initialized: Arc::new(RwLock::new(false)),
}
}
pub fn from_parsed(parsed: super::ParsedTopology, role: RoleName) -> Self {
Self::new(parsed.topology, role)
}
pub fn role(&self) -> &RoleName {
&self.role
}
pub fn topology(&self) -> &Topology {
&self.topology
}
pub async fn initialize(&self) -> TransportResult<()> {
let mut transports = write_lock!(self.transports);
let mut initialized = write_lock!(self.initialized);
if *initialized {
return Ok(());
}
for role in self.topology.locations.keys() {
if role != &self.role {
let transport = TransportFactory::create(&self.topology, role);
transports.insert(role.clone(), transport);
}
}
*initialized = true;
Ok(())
}
pub async fn send(&self, to_role: &RoleName, message: Vec<u8>) -> TransportResult<()> {
let transports = read_lock!(self.transports);
if let Some(transport) = transports.get(to_role) {
transport.send(to_role, message).await
} else {
drop(transports);
let mut transports = write_lock!(self.transports);
let transport = TransportFactory::create(&self.topology, to_role);
transports.insert(to_role.clone(), transport);
transports
.get(to_role)
.ok_or_else(|| TransportError::UnknownRole(to_role.clone()))?
.send(to_role, message)
.await
}
}
pub async fn recv(&self, from_role: &RoleName) -> TransportResult<Vec<u8>> {
let transports = read_lock!(self.transports);
if let Some(transport) = transports.get(from_role) {
transport.recv(from_role).await
} else {
Err(TransportError::UnknownRole(from_role.clone()))
}
}
pub async fn is_connected(&self, role: &RoleName) -> Result<bool, TopologyError> {
if self.topology.is_local(role)? {
return Ok(true);
}
let transports = read_lock!(self.transports);
if let Some(transport) = transports.get(role) {
Ok(transport.is_connected(role))
} else {
Ok(false)
}
}
pub fn get_location(&self, role: &RoleName) -> Result<Location, TopologyError> {
self.topology.get_location(role)
}
pub async fn close(&self) -> TransportResult<()> {
let mut transports = write_lock!(self.transports);
for (_, transport) in transports.iter() {
transport.close().await?;
}
transports.clear();
*write_lock!(self.initialized) = false;
Ok(())
}
}
pub struct TopologyHandlerBuilder {
topology: Topology,
role: Option<RoleName>,
}
#[derive(Debug, Error)]
pub enum TopologyHandlerBuildError {
#[error("role not specified for topology handler")]
MissingRole,
}
impl TopologyHandlerBuilder {
pub fn new(topology: Topology) -> Self {
Self {
topology,
role: None,
}
}
pub fn with_role(mut self, role: RoleName) -> Self {
self.role = Some(role);
self
}
pub fn build(self) -> Result<TopologyHandler, TopologyHandlerBuildError> {
let role = self.role.ok_or(TopologyHandlerBuildError::MissingRole)?;
Ok(TopologyHandler::new(self.topology, role))
}
}
impl TopologyHandler {
pub fn local(role: RoleName) -> Self {
Self::new(Topology::local_mode(), role)
}
pub fn builder(topology: Topology) -> TopologyHandlerBuilder {
TopologyHandlerBuilder::new(topology)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_topology_handler_creation() {
let topology = Topology::builder()
.local_role(RoleName::from_static("Alice"))
.local_role(RoleName::from_static("Bob"))
.build();
let handler = TopologyHandler::new(topology, RoleName::from_static("Alice"));
assert_eq!(handler.role(), &RoleName::from_static("Alice"));
}
#[test]
fn test_local_handler() {
let handler = TopologyHandler::local(RoleName::from_static("Alice"));
assert_eq!(handler.role(), &RoleName::from_static("Alice"));
assert!(handler.topology().mode.is_some());
}
#[test]
fn test_handler_builder() {
let topology = Topology::builder()
.remote_role(
RoleName::from_static("Alice"),
crate::identifiers::Endpoint::new("localhost:8080").unwrap(),
)
.remote_role(
RoleName::from_static("Bob"),
crate::identifiers::Endpoint::new("localhost:8081").unwrap(),
)
.build();
let handler = TopologyHandler::builder(topology)
.with_role(RoleName::from_static("Alice"))
.build()
.unwrap();
assert_eq!(handler.role(), &RoleName::from_static("Alice"));
}
#[test]
fn test_get_location() {
let topology = Topology::builder()
.local_role(RoleName::from_static("Alice"))
.remote_role(
RoleName::from_static("Bob"),
crate::identifiers::Endpoint::new("localhost:8080").unwrap(),
)
.build();
let handler = TopologyHandler::new(topology, RoleName::from_static("Alice"));
assert_eq!(
handler
.get_location(&RoleName::from_static("Alice"))
.unwrap(),
Location::Local
);
assert_eq!(
handler.get_location(&RoleName::from_static("Bob")).unwrap(),
Location::Remote(crate::identifiers::Endpoint::new("localhost:8080").unwrap())
);
}
}