use serde::de::DeserializeOwned;
use crate::{ActionContext, Context, Extractor, Frame, MessageFrame, PathError};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Path<T>(pub T)
where
T: DeserializeOwned;
impl<State, T> Extractor<State> for Path<T>
where
T: DeserializeOwned,
State: std::fmt::Debug + Clone + Send + Sync + 'static,
{
type Error = PathError;
fn extract(frame: Frame, context: &Context<State>) -> Result<Self, Self::Error> {
match (frame, &context.action) {
(Frame::Message(MessageFrame { uri, .. }), ActionContext::System(system_context)) => {
let capture = system_context.router.capture::<T>(uri)?;
Ok(Self(capture))
}
(_, ActionContext::System(_)) => Err(PathError::FrameIsNotAMessage),
_ => Err(PathError::IncorrectActionContext),
}
}
}
#[tokio::test]
async fn attempts_to_extract_given_types() -> Result<(), tower::BoxError> {
use crate::SystemContext;
let id = uuid::Uuid::new_v4();
let frame = Frame::message(format!("/uri/{id}"), (), ());
let mut router = crate::Router::<()>::new();
router.insert("/uri/:id", || async {})?;
let context = Context::from_action(SystemContext::from(router).into());
let Path((extracted,)): Path<(uuid::Uuid,)> = Path::extract(frame, &context)?;
assert_eq!(extracted, id);
Ok(())
}
#[tokio::test]
async fn non_frame_messages_error() -> Result<(), tower::BoxError> {
use crate::{Router, SystemContext};
let frame = Frame::default();
let context = Context::from_action(SystemContext::<()>::from(Router::default()).into());
let attempt = Path::<()>::extract(frame, &context);
assert!(
matches!(attempt.unwrap_err(), PathError::FrameIsNotAMessage),
"expected a frame is not a message error"
);
Ok(())
}
#[tokio::test]
async fn non_system_contexts_error() -> Result<(), tower::BoxError> {
let frame = Frame::default();
let context = Context::<()>::default();
let attempt = Path::<()>::extract(frame, &context);
assert!(
matches!(attempt.unwrap_err(), PathError::IncorrectActionContext),
"expected an incorrect action context error"
);
Ok(())
}