From 34146f31391f3e0185c2c418f1b6e95823c02d8e Mon Sep 17 00:00:00 2001 From: NicolaLS <90551797+NicolaLS@users.noreply.github.com> Date: Fri, 1 Jul 2022 10:37:41 +0200 Subject: [PATCH] Implement `TryFrom` for `MethodFilter` (#1130) * implement TryFrom for MethodFilter * test for TryFrom for MethodFilter * 'UnsupportedMethod' error type for MethodFilter * Log TryFrom for MethodFilter * adjust docs * move docs Co-authored-by: David Pedersen --- axum/CHANGELOG.md | 2 + axum/src/routing/method_filter.rs | 97 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/axum/CHANGELOG.md b/axum/CHANGELOG.md index 82413421..5ec8d097 100644 --- a/axum/CHANGELOG.md +++ b/axum/CHANGELOG.md @@ -16,7 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `/foo/`). That is no longer supported and such requests will now be sent to the fallback. Consider using `axum_extra::routing::RouterExt::route_with_tsr` if you want the old behavior ([#1119]) +- **added** Implement `TryFrom` for `MethodFilter` and use new `NoMatchingMethodFilter` error in case of failure ([#1130]) +[#1130]: https://github.com/tokio-rs/axum/pull/1130 [#1077]: https://github.com/tokio-rs/axum/pull/1077 [#1102]: https://github.com/tokio-rs/axum/pull/1102 [#1119]: https://github.com/tokio-rs/axum/pull/1119 diff --git a/axum/src/routing/method_filter.rs b/axum/src/routing/method_filter.rs index 82e823a5..ca9b0c06 100644 --- a/axum/src/routing/method_filter.rs +++ b/axum/src/routing/method_filter.rs @@ -1,4 +1,9 @@ use bitflags::bitflags; +use http::Method; +use std::{ + fmt, + fmt::{Debug, Formatter}, +}; bitflags! { /// A filter that matches one or more HTTP methods. @@ -21,3 +26,95 @@ bitflags! { const TRACE = 0b100000000; } } + +/// Error type used when converting a [`Method`] to a [`MethodFilter`] fails. +#[derive(Debug)] +pub struct NoMatchingMethodFilter { + method: Method, +} + +impl NoMatchingMethodFilter { + /// Get the [`Method`] that couldn't be converted to a [`MethodFilter`]. + pub fn method(&self) -> &Method { + &self.method + } +} + +impl fmt::Display for NoMatchingMethodFilter { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "no `MethodFilter` for `{}`", self.method.as_str()) + } +} + +impl std::error::Error for NoMatchingMethodFilter {} + +impl TryFrom for MethodFilter { + type Error = NoMatchingMethodFilter; + + fn try_from(m: Method) -> Result { + match m { + Method::DELETE => Ok(MethodFilter::DELETE), + Method::GET => Ok(MethodFilter::GET), + Method::HEAD => Ok(MethodFilter::HEAD), + Method::OPTIONS => Ok(MethodFilter::OPTIONS), + Method::PATCH => Ok(MethodFilter::PATCH), + Method::POST => Ok(MethodFilter::POST), + Method::PUT => Ok(MethodFilter::PUT), + Method::TRACE => Ok(MethodFilter::TRACE), + other => Err(NoMatchingMethodFilter { method: other }), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_http_method() { + assert_eq!( + MethodFilter::try_from(Method::DELETE).unwrap(), + MethodFilter::DELETE + ); + + assert_eq!( + MethodFilter::try_from(Method::GET).unwrap(), + MethodFilter::GET + ); + + assert_eq!( + MethodFilter::try_from(Method::HEAD).unwrap(), + MethodFilter::HEAD + ); + + assert_eq!( + MethodFilter::try_from(Method::OPTIONS).unwrap(), + MethodFilter::OPTIONS + ); + + assert_eq!( + MethodFilter::try_from(Method::PATCH).unwrap(), + MethodFilter::PATCH + ); + + assert_eq!( + MethodFilter::try_from(Method::POST).unwrap(), + MethodFilter::POST + ); + + assert_eq!( + MethodFilter::try_from(Method::PUT).unwrap(), + MethodFilter::PUT + ); + + assert_eq!( + MethodFilter::try_from(Method::TRACE).unwrap(), + MethodFilter::TRACE + ); + + assert!(MethodFilter::try_from(http::Method::CONNECT) + .unwrap_err() + .to_string() + .contains("CONNECT")); + } +}