mod connection;
mod error;
mod reporter;
pub use crate::connection::{
HEARTBEAT_IVL_MS, HEARTBEAT_TIMEOUT_MS, POLL_INTERVAL_MS, RECONNECT_MAX_MS,
RECONNECT_MIN_MS,
};
pub use crate::error::Error;
use crate::connection::{Connection, Stall};
use crate::reporter::Reporter;
use chrono::prelude::*;
use elite_journal::entry::{Entry, Event, Market};
use miniz_oxide::inflate;
use serde::Deserialize;
use std::thread;
use std::time::Duration;
use tracing::{info, warn, Level};
pub const URL: &'static str = "tcp://eddn.edcd.io:9500";
#[derive(Debug, Deserialize)]
pub struct Envelope {
#[serde(rename = "$schemaRef")]
pub schema_ref: String,
pub header: Header,
pub message: Message,
}
#[derive(Debug, Deserialize)]
pub struct Header {
#[serde(rename = "gatewayTimestamp")]
pub gateway_timestamp: DateTime<Utc>,
#[serde(rename = "softwareName")]
pub software_name: String,
#[serde(rename = "softwareVersion")]
pub software_version: String,
#[serde(rename = "uploaderID")]
pub uploader_id: String,
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Message {
Journal(Entry<Event>),
Commodity(Entry<Market>),
Other(serde_json::Value),
}
pub fn subscribe(
url: &str,
stall_timeout: Option<Duration>,
) -> EnvelopeIterator {
let ctx = zmq::Context::new();
let connection =
Connection::open(&ctx, url).expect("failed to open socket");
info!("Subscribed to {}", url);
EnvelopeIterator {
ctx,
url: url.to_string(),
connection,
reports: Reporter::default(),
stall: stall_timeout.map(Stall::new),
}
}
pub struct EnvelopeIterator {
ctx: zmq::Context,
url: String,
connection: Connection,
reports: Reporter,
stall: Option<Stall>,
}
impl EnvelopeIterator {
fn reconnect(&mut self, reason: &str) {
warn!("{}, replacing the connection", reason);
loop {
match Connection::open(&self.ctx, &self.url) {
Ok(connection) => {
self.connection = connection;
if let Some(stall) = &mut self.stall {
stall.restart();
}
self.reports.replaced();
return;
}
Err(err) => {
warn!("Could not open a socket: {}", err);
thread::sleep(Duration::from_secs(5));
}
}
}
}
}
impl Iterator for EnvelopeIterator {
type Item = Result<Envelope, Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
let events = self.connection.events();
for note in self.reports.observe(&events) {
if note.level == Level::WARN {
warn!("{}", note.message);
} else {
info!("{}", note.message);
}
}
match self.connection.socket.recv_bytes(0) {
Ok(compressed) => {
if let Some(stall) = &mut self.stall {
stall.restart();
}
return Some(
inflate::decompress_to_vec_zlib(&compressed)
.map_err(Error::Decompress)
.and_then(|json| {
serde_json::from_slice(&json)
.map_err(Error::Parse)
}),
);
}
Err(zmq::Error::EAGAIN) => {
let overrun = self.stall.as_ref().and_then(Stall::overrun);
if let Some(quiet) = overrun {
let reason =
format!("nothing for {}s", quiet.as_secs());
self.reconnect(&reason);
}
}
Err(zmq::Error::EINTR) => {}
Err(err) => {
self.reconnect(&format!("socket error: {}", err));
return Some(Err(Error::Socket(err)));
}
}
}
}
}