use std::fmt;
use async_trait::async_trait;
use crate::model::{Question, UpstreamGroupId};
#[async_trait]
pub trait RouteHook: Send + Sync {
async fn select(
&self,
request: RouteRequest<'_>,
) -> std::result::Result<RouteDecision, RouteHookError>;
}
pub struct RouteRequest<'a> {
question: &'a Question,
static_group: Option<&'a UpstreamGroupId>,
}
impl<'a> RouteRequest<'a> {
#[allow(dead_code)]
pub(crate) fn new(question: &'a Question, static_group: Option<&'a UpstreamGroupId>) -> Self {
Self {
question,
static_group,
}
}
pub fn question(&self) -> &'a Question {
self.question
}
pub fn static_group(&self) -> Option<&'a UpstreamGroupId> {
self.static_group
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RouteDecision {
Use(UpstreamGroupId),
Abstain,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RouteHookError {
message: String,
}
impl RouteHookError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl fmt::Display for RouteHookError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for RouteHookError {}
#[cfg(test)]
mod tests {
use super::{RouteDecision, RouteHook, RouteHookError, RouteRequest};
use crate::model::UpstreamGroupId;
use async_trait::async_trait;
struct AbstainingHook;
#[async_trait]
impl RouteHook for AbstainingHook {
async fn select(
&self,
_request: RouteRequest<'_>,
) -> std::result::Result<RouteDecision, RouteHookError> {
Ok(RouteDecision::Abstain)
}
}
#[test]
fn module_types_are_importable_and_route_hook_is_dyn_compatible() {
fn accepts_dyn_hook(_: &dyn RouteHook) {}
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<AbstainingHook>();
accepts_dyn_hook(&AbstainingHook);
assert_eq!(
RouteDecision::Use(UpstreamGroupId::new("alternate")),
RouteDecision::Use(UpstreamGroupId::new("alternate"))
);
}
#[test]
fn hook_error_has_a_stable_display_message_and_implements_std_error() {
let error = RouteHookError::new("policy service unavailable");
assert_eq!(error.to_string(), "policy service unavailable");
fn assert_std_error<E: std::error::Error>(_: &E) {}
assert_std_error(&error);
}
}