use std::sync::{Arc, Mutex, PoisonError};
use std::time::Duration;
use serde_json::Value;
use crate::ui::{Channel, Model};
mod traffic;
pub use traffic::{Heard, Said, Standing};
#[derive(Default)]
struct Shared {
heard: Vec<Heard>,
outbox: Vec<Value>,
standing: Standing,
stopped: bool,
}
#[derive(Clone)]
pub struct Link {
shared: Arc<Mutex<Shared>>,
beat: Duration,
}
impl Link {
pub fn new(beat: Duration) -> Self {
Self {
shared: Arc::new(Mutex::new(Shared::default())),
beat,
}
}
pub fn beat(&self) -> Duration {
self.beat
}
pub fn settle(&self, model: &mut Model) {
let mut shared = self.hold();
for heard in std::mem::take(&mut shared.heard) {
match heard.said {
Said::Frame(frame) => model.absorb(&heard.channel, crate::reply::read(&frame)),
Said::Live { conversation, read } => {
if model.conversation.as_deref() == Some(conversation.as_str()) {
model.absorb(&heard.channel, read);
}
}
Said::Unreachable(why) => model.unreachable(&heard.channel, why),
}
}
shared.outbox.append(&mut model.outbox);
shared.standing = Standing::of(model);
}
pub fn heard(&self, channel: &Channel, said: Said) {
self.hold().heard.push(Heard {
channel: channel.clone(),
said,
});
}
pub fn live(&self, channel: &Channel, conversation: &str, read: crate::reply::Read) {
self.heard(
channel,
Said::Live {
conversation: conversation.to_owned(),
read,
},
);
}
pub fn standing(&self) -> Standing {
self.hold().standing.clone()
}
pub fn compose(&self) -> Vec<Value> {
std::mem::take(&mut self.hold().outbox)
}
pub fn stop(&self) {
self.hold().stopped = true;
}
pub fn stopped(&self) -> bool {
self.hold().stopped
}
fn hold(&self) -> std::sync::MutexGuard<'_, Shared> {
self.shared.lock().unwrap_or_else(PoisonError::into_inner)
}
}
#[cfg(test)]
mod tests;