use crate::transport::{
error::{TransportError, TransportResult},
memory_mock::memory_server,
protocol::{TransportCommand, TransportEvent},
transport_trait::Transport,
TransportId, TransportIdRef,
};
use lib3h_protocol::DidWork;
use std::collections::{HashMap, HashSet, VecDeque};
pub struct TransportMemory {
cmd_inbox: VecDeque<TransportCommand>,
my_servers: HashSet<String>,
connections: HashMap<TransportId, String>,
n_id: u32,
maybe_my_uri: Option<String>,
}
impl TransportMemory {
pub fn new() -> Self {
TransportMemory {
cmd_inbox: VecDeque::new(),
my_servers: HashSet::new(),
connections: HashMap::new(),
n_id: 0,
maybe_my_uri: None,
}
}
pub fn name(&self) -> &str {
match &self.maybe_my_uri {
None => "",
Some(uri) => uri,
}
}
}
impl Transport for TransportMemory {
fn transport_id_list(&self) -> TransportResult<Vec<TransportId>> {
Ok(self.connections.keys().map(|id| id.to_string()).collect())
}
fn get_uri(&self, id: &TransportIdRef) -> Option<String> {
let res = self.connections.get(&id.to_string());
res.map(|url| url.to_string())
}
fn connect(&mut self, uri: &str) -> TransportResult<TransportId> {
let my_uri = match &self.maybe_my_uri {
None => {
return Err(TransportError::new(
"Must bind before connecting".to_string(),
));
}
Some(u) => u,
};
let server_map = memory_server::MEMORY_SERVER_MAP.read().unwrap();
let maybe_server = server_map.get(uri);
if let None = maybe_server {
return Err(TransportError::new(format!(
"No Memory server at this url address: {}",
uri
)));
}
self.n_id += 1;
let id = format!("mem_conn_{}", self.n_id);
let mut server = maybe_server.unwrap().lock().unwrap();
server.connect(&my_uri)?;
self.connections.insert(id.clone(), uri.to_string());
Ok(id)
}
fn close(&mut self, id: &TransportIdRef) -> TransportResult<()> {
let maybe_url = self.connections.get(id);
if let None = maybe_url {
return Err(TransportError::new(format!(
"No known connection for TransportId {}",
id
)));
}
let url = maybe_url.unwrap();
let server_map = memory_server::MEMORY_SERVER_MAP.read().unwrap();
let maybe_server = server_map.get(url);
if let None = maybe_server {
return Err(TransportError::new(format!(
"No Memory server at this url: {}",
url,
)));
}
let mut server = maybe_server.unwrap().lock().unwrap();
server.close(&id)?;
self.connections.remove(id);
Ok(())
}
fn close_all(&mut self) -> TransportResult<()> {
let id_list = self.transport_id_list()?;
for id in id_list {
self.close(&id)?;
}
Ok(())
}
fn send(&mut self, id_list: &[&TransportIdRef], payload: &[u8]) -> TransportResult<()> {
for id in id_list {
let maybe_uri = self.connections.get(*id);
if let None = maybe_uri {
println!("[w] No known connection for TransportId: {}", id);
continue;
}
let uri = maybe_uri.unwrap();
let server_map = memory_server::MEMORY_SERVER_MAP.read().unwrap();
let maybe_server = server_map.get(uri);
if let None = maybe_server {
return Err(TransportError::new(format!(
"No Memory server at this url address: {}",
uri
)));
}
println!("[t] (TransportMemory).send() {} | {}", uri, payload.len());
let mut server = maybe_server.unwrap().lock().unwrap();
server
.post(&self.maybe_my_uri.clone().unwrap(), payload)
.expect("Post on memory server should work");
}
Ok(())
}
fn send_all(&mut self, payload: &[u8]) -> TransportResult<()> {
let id_list = self.transport_id_list()?;
for id in id_list {
self.send(&[id.as_str()], payload)?;
}
Ok(())
}
fn post(&mut self, command: TransportCommand) -> TransportResult<()> {
self.cmd_inbox.push_back(command);
Ok(())
}
fn bind(&mut self, uri: &str) -> TransportResult<String> {
let bounded_uri = format!("{}_bound", uri);
self.maybe_my_uri = Some(bounded_uri.clone());
memory_server::set_server(&bounded_uri)?;
self.my_servers.insert(bounded_uri.to_string());
Ok(bounded_uri.to_string())
}
fn process(&mut self) -> TransportResult<(DidWork, Vec<TransportEvent>)> {
let mut outbox = Vec::new();
let mut did_work = false;
loop {
let cmd = match self.cmd_inbox.pop_front() {
None => break,
Some(msg) => msg,
};
let res = self.serve_TransportCommand(&cmd);
if let Ok(mut output) = res {
did_work = true;
outbox.append(&mut output);
}
}
let mut to_connect_list = Vec::new();
for server_uri in &self.my_servers {
let server_map = memory_server::MEMORY_SERVER_MAP.read().unwrap();
let server = server_map.get(server_uri).expect("My server should exist.");
let (success, output) = server.lock().unwrap().process()?;
if success {
did_work = true;
for event in &output {
if let TransportEvent::ConnectResult(uri) = event {
to_connect_list.push(uri.clone());
} else {
outbox.push(event.clone());
}
}
}
}
for uri in to_connect_list {
println!(
"[t] (TransportMemory) {} <- {}",
uri,
self.maybe_my_uri.clone().unwrap()
);
if let Ok(id) = self.connect(&uri) {
outbox.push(TransportEvent::ConnectResult(id));
}
}
Ok((did_work, outbox))
}
}
impl Drop for TransportMemory {
fn drop(&mut self) {
self.close_all().ok();
for bounded_url in &self.my_servers {
memory_server::unset_server(&bounded_url)
.expect("unset_server() during drop should never fail");
}
}
}
impl TransportMemory {
#[allow(non_snake_case)]
fn serve_TransportCommand(
&mut self,
cmd: &TransportCommand,
) -> TransportResult<Vec<TransportEvent>> {
println!("[d] >>> '(TransportMemory)' recv cmd: {:?}", cmd);
match cmd {
TransportCommand::Connect(url) => {
let id = self.connect(url)?;
let evt = TransportEvent::ConnectResult(id);
Ok(vec![evt])
}
TransportCommand::Send(id_list, payload) => {
let mut id_ref_list = Vec::with_capacity(id_list.len());
for id in id_list {
id_ref_list.push(id.as_str());
}
let _id = self.send(&id_ref_list, payload)?;
Ok(vec![])
}
TransportCommand::SendAll(payload) => {
let _id = self.send_all(payload)?;
Ok(vec![])
}
TransportCommand::Close(id) => {
self.close(id)?;
let evt = TransportEvent::Closed(id.to_string());
Ok(vec![evt])
}
TransportCommand::CloseAll => {
self.close_all()?;
let mut outbox = Vec::new();
for (id, _url) in &self.connections {
let evt = TransportEvent::Closed(id.to_string());
outbox.push(evt);
}
Ok(outbox)
}
TransportCommand::Bind(url) => {
self.bind(url)?;
Ok(vec![])
}
}
}
}