use crate::gene::GeneType;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use time::OffsetDateTime;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
#[derive(Debug, Serialize, Deserialize)]
pub struct GeneMapping {
pub path: String,
pub gene_type: GeneType,
}
pub type NvResult<T> = Result<T, NvError>;
#[derive(Debug, Clone)]
pub struct NvError {
pub reason: String,
}
impl fmt::Display for NvError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.reason)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PathQuery {
pub path: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Observations {
pub datetime: String,
pub values: HashMap<i32, f64>,
pub path: String,
}
#[derive(Debug)]
pub struct Envelope<T> {
pub message: Message<T>,
pub respond_to: Option<oneshot::Sender<NvResult<Message<T>>>>,
pub datetime: OffsetDateTime,
pub stream_to: Option<mpsc::Sender<Message<T>>>,
pub stream_from: Option<mpsc::Receiver<Message<T>>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum MtHint {
Update,
Query,
State,
GeneMapping,
GeneMappingQuery,
}
impl fmt::Display for MtHint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let display_text = match self {
Self::Query => "query",
Self::State => "state",
Self::Update => "update",
Self::GeneMapping => "gene mapping",
Self::GeneMappingQuery => "gene mapping query",
};
write!(f, "[{display_text}]")
}
}
#[derive(Debug, Clone)]
pub enum Message<T> {
Query {
path: String,
hint: MtHint,
},
Update {
datetime: OffsetDateTime,
path: String,
values: HashMap<i32, T>,
},
StateReport {
datetime: OffsetDateTime,
path: String,
values: HashMap<i32, T>,
},
GeneMapping {
path: String,
gene_type: GeneType,
},
EndOfStream {},
Persisted,
NotFound {
path: String,
},
ConstraintViolation,
InitCmd {
hint: MtHint,
},
LoadCmd {
path: String,
hint: MtHint,
},
ReadAllCmd {},
Content {
text: String,
hint: MtHint,
path: Option<String>,
},
}
impl<T> fmt::Display for Envelope<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let display_text = format!("env: {} - {}", self.datetime, self.message);
write!(f, "{display_text}")
}
}
impl<T> fmt::Display for Message<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let display_text = match self {
Self::Content { text, hint, path } => path.clone().map_or_else(
|| format!("[Content {hint}:{text}]"),
|path| format!("[Content {path} {hint}:{text}]"),
),
Self::LoadCmd { path, hint } => format!("[LoadCmd {path} {hint}]"),
Self::ReadAllCmd {} => "[ReadAllCmd]".to_string(),
Self::InitCmd { hint } => format!("[InitCmd {hint}]"),
Self::EndOfStream {} => "[EndOfStream]".to_string(),
Self::Persisted {} => "[Persisted]".to_string(),
Self::NotFound { path } => "[Not Found]".to_string(),
Self::ConstraintViolation {} => "[Contraint Violation]".to_string(),
Self::StateReport { .. } => "[StateReport]".to_string(), Self::GeneMapping { .. } => "[GeneMapping]".to_string(), Self::Update { .. } => "[Update]".to_string(),
Self::Query { .. } => "[Query]".to_string(),
};
write!(f, "{display_text}")
}
}
impl<T> Default for Envelope<T> {
fn default() -> Self {
Self {
message: Message::ReadAllCmd {},
respond_to: None,
datetime: OffsetDateTime::now_utc(),
stream_to: None,
stream_from: None,
}
}
}
struct LifeCycleBuilder<T> {
load_from: Option<mpsc::Receiver<Message<T>>>,
send_to: Option<mpsc::Sender<Message<T>>>,
send_to_path: Option<String>,
respond_to: Option<oneshot::Sender<NvResult<Message<T>>>>,
hint: Option<MtHint>,
}
impl<T> LifeCycleBuilder<T> {
const fn new() -> Self {
Self {
hint: None,
load_from: None,
send_to: None,
send_to_path: None,
respond_to: None,
}
}
#[allow(clippy::missing_const_for_fn)]
fn with_respond_to(mut self, respond_to: oneshot::Sender<NvResult<Message<T>>>) -> Self {
self.respond_to = Some(respond_to);
self
}
#[allow(clippy::missing_const_for_fn)]
fn with_load_from(mut self, load_from: mpsc::Receiver<Message<T>>) -> Self {
self.load_from = Some(load_from);
self
}
#[allow(clippy::missing_const_for_fn)]
fn with_send_to(mut self, send_to: mpsc::Sender<Message<T>>, send_to_path: String) -> Self {
self.send_to = Some(send_to);
self.send_to_path = Some(send_to_path);
self
}
#[allow(clippy::missing_const_for_fn)]
fn with_hint(mut self, hint: MtHint) -> Self {
self.hint = Some(hint);
self
}
fn build(self) -> (Envelope<T>, Envelope<T>) {
(
Envelope {
datetime: OffsetDateTime::now_utc(),
respond_to: self.respond_to,
stream_from: self.load_from,
stream_to: None,
message: Message::InitCmd {
hint: self.hint.unwrap_or(MtHint::Update),
},
},
Envelope {
datetime: OffsetDateTime::now_utc(),
respond_to: None,
stream_from: None,
stream_to: self.send_to,
message: Message::LoadCmd {
hint: self.hint.unwrap_or(MtHint::Update),
path: self.send_to_path.unwrap_or_default(),
},
},
)
}
}
#[must_use]
pub fn create_init_lifecycle<T>(
path: String,
bufsz: usize,
respond_to: oneshot::Sender<NvResult<Message<T>>>,
hint: MtHint,
) -> (Envelope<T>, Envelope<T>) {
let (tx, rx) = mpsc::channel(bufsz);
let builder = LifeCycleBuilder::new()
.with_load_from(rx)
.with_send_to(tx, path)
.with_respond_to(respond_to)
.with_hint(hint);
builder.build()
}