use roto::{Runtime, Val, roto_method, roto_static_method};
use serde::Serialize;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::Arc;
use crate::means_of_production::{Error, Outcome};
#[derive(Debug, Clone, Default, Serialize, PartialEq)]
pub struct Metadata(pub HashMap<String, String>);
pub type MutableMetadata = Rc<RefCell<Metadata>>;
#[derive(Debug, Serialize, Clone)]
pub struct Log {
#[serde(rename = "_msg")]
pub msg: String,
pub service: String,
pub verdict: LogVerdict,
#[serde(flatten)]
pub metadata: MutableMetadata,
}
impl Default for Log {
fn default() -> Self {
Self {
msg: String::default(),
service: "iocaine".to_owned(),
verdict: LogVerdict::default(),
metadata: MutableMetadata::default(),
}
}
}
#[derive(Debug, Serialize, Default, Clone)]
pub struct LogVerdict {
pub r#type: Arc<str>,
#[serde(flatten)]
pub outcome: Outcome,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct LogLine(Rc<RefCell<Log>>);
impl LogLine {
pub fn to_json(&self) -> Result<String, ()> {
serde_json::to_string(&self.0.borrow().clone()).map_err(|e| {
tracing::error!("Unable to format LogLine to JSON: {e}");
})
}
}
pub fn register_log(runtime: &mut Runtime) -> Result<(), Error> {
runtime.register_clone_type_with_name::<LogLine>("Logger", "Logging framework")?;
runtime.register_clone_type_with_name::<MutableMetadata>(
"Metadata",
"Metadata (key-value pairs) associated with a log message",
)?;
{
#[roto_static_method(runtime, MutableMetadata)]
fn new() -> Val<MutableMetadata> {
Rc::new(RefCell::new(Metadata::default())).into()
}
#[roto_static_method(runtime, MutableMetadata)]
fn with(key: Arc<str>, value: Arc<str>) -> Val<MutableMetadata> {
Rc::new(RefCell::new(Metadata(HashMap::from([(
key.to_string(),
value.to_string(),
)]))))
.into()
}
{
#[roto_method(runtime, MutableMetadata)]
fn with(
md: Val<MutableMetadata>,
key: Arc<str>,
value: Arc<str>,
) -> Val<MutableMetadata> {
(*md)
.borrow_mut()
.0
.insert(key.to_string(), value.to_string());
md
}
}
}
{
#[roto_static_method(runtime, LogLine)]
fn with_metadata(metadata: Val<MutableMetadata>) -> Val<LogLine> {
LogLine(Rc::new(RefCell::new(Log {
service: "iocaine".to_owned(),
msg: "poisoning request: unknown".to_owned(),
metadata: metadata.0,
..Default::default()
})))
.into()
}
#[roto_static_method(runtime, LogLine)]
fn with_verdict(verdict: Arc<str>, outcome: Val<Outcome>) -> Val<LogLine> {
LogLine(Rc::new(RefCell::new(Log {
service: "iocaine".to_owned(),
msg: format!("poisoning request: {verdict}"),
verdict: LogVerdict {
r#type: verdict,
outcome: outcome.0,
},
..Default::default()
})))
.into()
}
#[roto_static_method(runtime, LogLine)]
fn new() -> Val<LogLine> {
LogLine::default().into()
}
#[roto_static_method(runtime, LogLine)]
fn debug(msg: Arc<str>) {
tracing::debug!("{msg}");
}
}
#[roto_method(runtime, LogLine)]
fn with_metadata(log: Val<LogLine>, metadata: Val<MutableMetadata>) -> Val<LogLine> {
(*log).0.borrow_mut().metadata = metadata.0;
log
}
#[roto_method(runtime, LogLine)]
fn with_verdict(log: Val<LogLine>, verdict: Arc<str>, outcome: Val<Outcome>) -> Val<LogLine> {
{
let mut line = log.0.0.borrow_mut();
line.msg = format!("poisoning request: {verdict}");
line.verdict = LogVerdict {
r#type: verdict,
outcome: outcome.0,
};
}
log
}
#[roto_method(runtime, LogLine)]
fn stdout(log: Val<LogLine>) {
let _ = (*log).to_json().map(|json| println!("{json}"));
}
#[roto_method(runtime, LogLine)]
fn to_json(log: Val<LogLine>) -> Arc<str> {
#[allow(clippy::ignored_unit_patterns)]
(*log).to_json().map_or_else(|_| Arc::default(), Arc::from)
}
Ok(())
}