use std::any::{Any, TypeId};
use std::collections::HashMap;
use crate::Headers;
use super::dispatch::Delivery;
use super::publish::ScopedPublisher;
#[derive(Default)]
pub struct State {
map: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}
impl State {
pub fn insert<T: Any + Send + Sync>(&mut self, value: T) {
self.map.insert(TypeId::of::<T>(), Box::new(value));
}
#[must_use]
pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
self.map.get(&TypeId::of::<T>())?.downcast_ref::<T>()
}
}
impl std::fmt::Debug for State {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("State")
.field("entries", &self.map.len())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct Context<'a> {
name: &'a str,
original: &'a Headers,
modified: Option<Headers>,
state: &'a State,
delivery: &'a Delivery,
}
impl<'a> Context<'a> {
pub(crate) fn new(
name: &'a str,
headers: &'a Headers,
state: &'a State,
delivery: &'a Delivery,
) -> Self {
Self {
name,
original: headers,
modified: None,
state,
delivery,
}
}
#[must_use]
pub fn name(&self) -> &str {
self.name
}
#[must_use]
pub fn publisher(&self, name: &str) -> Option<ScopedPublisher<'_>> {
let publisher = self.delivery.publishers.get(name)?;
Some(ScopedPublisher::new(
publisher.as_ref(),
&self.delivery.pipeline,
))
}
#[must_use]
pub fn headers(&self) -> &Headers {
self.modified.as_ref().unwrap_or(self.original)
}
pub fn headers_mut(&mut self) -> &mut Headers {
self.modified.get_or_insert_with(|| self.original.clone())
}
#[must_use]
pub fn get<T: Any + Send + Sync>(&self) -> Option<&T> {
self.state.get::<T>()
}
}
#[cfg(test)]
mod tests {
use super::{Context, State};
use crate::Headers;
use crate::runtime::dispatch::Delivery;
#[test]
fn headers_clone_only_on_first_mutation() {
let mut original = Headers::new();
original.insert("k", "v");
let state = State::default();
let delivery = Delivery::empty();
let mut ctx = Context::new("test", &original, &state, &delivery);
assert!(std::ptr::eq(ctx.headers(), &raw const original));
ctx.headers_mut().insert("added", "1");
ctx.headers_mut().insert("added2", "2");
assert!(!std::ptr::eq(ctx.headers(), &raw const original));
assert_eq!(ctx.headers().get("added"), Some(&b"1"[..]));
assert_eq!(ctx.headers().get("k"), Some(&b"v"[..]));
assert_eq!(original.get("added"), None);
}
}