use std::sync::{Arc, RwLock};
use rainier_container::Container;
use crate::pipeline::Middleware;
use crate::stack::{IntoMiddlewareStack, MiddlewareStack};
#[derive(Default)]
pub struct MiddlewareRegistry {
global: RwLock<MiddlewareStack>,
}
impl MiddlewareRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn global(&self, middleware: impl IntoMiddlewareStack) {
let mut global = self.global.write().expect("registry lock poisoned");
let existing = std::mem::take(&mut *global);
*global = existing.with_stack(middleware.into_middleware_stack());
}
pub fn global_arc(&self, middleware: Arc<dyn Middleware>) {
let mut global = self.global.write().expect("registry lock poisoned");
let existing = std::mem::take(&mut *global);
*global = existing.with_arc(middleware);
}
pub fn global_middleware(&self) -> MiddlewareStack {
self.global.read().expect("registry lock poisoned").clone()
}
pub fn global_stack(
&self,
container: &Container,
) -> rainier_support::Result<Vec<Arc<dyn Middleware>>> {
self.global.read().expect("registry lock poisoned").resolve(container)
}
pub fn global_labels(&self) -> Vec<&'static str> {
self.global.read().expect("registry lock poisoned").labels()
}
}
impl std::fmt::Debug for MiddlewareRegistry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MiddlewareRegistry")
.field("global", &self.global.read().map(|g| g.labels()).unwrap_or_default())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pipeline::Next;
use rainier_http::{Request, Response};
struct Named(&'static str);
#[async_trait::async_trait]
impl Middleware for Named {
async fn handle(&self, request: Request, next: Next) -> Response {
next.run(request).await
}
fn name(&self) -> &'static str {
self.0
}
}
fn names(registry: &MiddlewareRegistry) -> Vec<&'static str> {
registry.global_stack(&Container::new()).unwrap().iter().map(|m| m.name()).collect()
}
#[test]
fn the_global_stack_keeps_its_order() {
let registry = MiddlewareRegistry::new();
registry.global(Named("first"));
registry.global(Named("second"));
assert_eq!(names(®istry), vec!["first", "second"]);
}
#[test]
fn a_tuple_registers_several_at_once_in_order() {
let registry = MiddlewareRegistry::new();
registry.global((Named("first"), Named("second")));
registry.global(Named("third"));
assert_eq!(names(®istry), vec!["first", "second", "third"]);
}
#[test]
fn a_whole_stack_can_be_registered_globally() {
let registry = MiddlewareRegistry::new();
registry.global(MiddlewareStack::new().with(Named("a")).with(Named("b")));
assert_eq!(names(®istry), vec!["a", "b"]);
}
#[test]
fn a_failing_deferred_global_stops_the_boot() {
let registry = MiddlewareRegistry::new();
registry.global(
MiddlewareStack::new()
.deferred(|_| Err::<Named, _>(rainier_support::Error::internal("nothing bound"))),
);
let err = registry.global_stack(&Container::new()).err().expect("should fail");
assert!(err.message().contains("nothing bound"), "{}", err.message());
}
#[test]
fn the_labels_are_readable_without_a_container() {
let registry = MiddlewareRegistry::new();
registry.global(Named("first"));
assert_eq!(registry.global_labels(), vec!["first"]);
}
}