use std::borrow::Cow;
use std::ops::Deref;
use axum::http::header;
use axum::http::HeaderValue;
use axum::routing::MethodFilter;
pub use axum::response::{
Html,
IntoResponse,
Json,
Response,
Redirect,
NoContent,
AppendHeaders,
};
pub use axum::routing::{
delete,
get,
patch,
post,
put,
Router as AxumRouter,
any,
method_routing::MethodRouter,
};
pub use axum::extract::{
Path,
State,
Extension,
Request,
Form,
RawQuery,
MatchedPath,
OriginalUri,
};
pub use axum_extra::extract::Query;
pub use axum::http::{
StatusCode,
HeaderMap,
HeaderName,
Method as HttpMethod,
Uri,
};
pub use axum::body::Body;
use serde::Deserialize;
use serde::Serialize;
use std::ops::{BitOr, BitOrAssign};
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Methods(MethodFilter);
impl serde::Serialize for Methods {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let methods = self.to_vec();
let mut seq = serializer.serialize_seq(Some(methods.len()))?;
for method in methods {
seq.serialize_element(method)?;
}
seq.end()
}
}
impl<'de> serde::Deserialize<'de> for Methods {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct MethodsVisitor;
impl<'de> serde::de::Visitor<'de> for MethodsVisitor {
type Value = Methods;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string or list of HTTP method names")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Methods::from_str(v).ok_or_else(|| E::custom(format!("unknown HTTP method: {}", v)))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut methods: Option<Methods> = None;
while let Some(method_str) = seq.next_element::<String>()? {
let method = Methods::from_str(&method_str).ok_or_else(|| {
serde::de::Error::custom(format!("unknown HTTP method: {}", method_str))
})?;
methods = Some(methods.map_or(method, |m| m | method));
}
methods.ok_or_else(|| serde::de::Error::custom("empty method list"))
}
}
deserializer.deserialize_any(MethodsVisitor)
}
}
impl Methods {
pub const GET: Self = Self(MethodFilter::GET);
pub const POST: Self = Self(MethodFilter::POST);
pub const PUT: Self = Self(MethodFilter::PUT);
pub const PATCH: Self = Self(MethodFilter::PATCH);
pub const DELETE: Self = Self(MethodFilter::DELETE);
pub const HEAD: Self = Self(MethodFilter::HEAD);
pub const OPTIONS: Self = Self(MethodFilter::OPTIONS);
pub const TRACE: Self = Self(MethodFilter::TRACE);
pub const CONNECT: Self = Self(MethodFilter::CONNECT);
pub fn iter(&self) -> MethodIter {
MethodIter {
filter: self.0,
position: 0,
}
}
pub fn or(self, other: Self) -> Self {
Self(self.0.or(other.0))
}
pub fn to_str(&self) -> Option<&'static str> {
KNOWN_METHODS.iter()
.find(|(_, f)| *f == self.0)
.map(|(name, _)| *name)
}
pub fn to_vec(&self) -> Vec<&'static str> {
self.iter().map(|(name, _)| name).collect()
}
pub fn from_str(s: &str) -> Option<Self> {
let upper = s.to_uppercase();
KNOWN_METHODS.iter()
.find(|(name, _)| *name == upper.as_str())
.map(|(_, filter)| Self(*filter))
}
}
impl BitOr for Methods {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0.or(rhs.0))
}
}
impl BitOrAssign for Methods {
fn bitor_assign(&mut self, rhs: Self) {
self.0 = self.0.or(rhs.0);
}
}
impl From<Methods> for MethodFilter {
fn from(methods: Methods) -> Self {
methods.0
}
}
impl From<MethodFilter> for Methods {
fn from(filter: MethodFilter) -> Self {
Self(filter)
}
}
pub struct MethodIter {
filter: MethodFilter,
position: usize,
}
impl Iterator for MethodIter {
type Item = (&'static str, Methods);
fn next(&mut self) -> Option<Self::Item> {
while self.position < KNOWN_METHODS.len() {
let (name, method) = KNOWN_METHODS[self.position];
self.position += 1;
let combined = self.filter.or(method);
if combined == self.filter {
return Some((name, Methods(method)));
}
}
None
}
}
impl Deref for Methods {
type Target = MethodFilter;
fn deref(&self) -> &Self::Target {
&self.0
}
}
const KNOWN_METHODS: &[(&str, MethodFilter)] = &[
("GET", MethodFilter::GET),
("POST", MethodFilter::POST),
("PUT", MethodFilter::PUT),
("PATCH", MethodFilter::PATCH),
("DELETE", MethodFilter::DELETE),
("HEAD", MethodFilter::HEAD),
("OPTIONS", MethodFilter::OPTIONS),
("TRACE", MethodFilter::TRACE),
("CONNECT", MethodFilter::CONNECT),
];
#[derive(Debug, Clone)]
pub struct JsonStr {
inner: Cow<'static, str>,
}
impl From<&'static str> for JsonStr {
fn from(value: &'static str) -> Self {
Self {
inner: Cow::Borrowed(value),
}
}
}
impl From<String> for JsonStr {
fn from(value: String) -> Self {
Self {
inner: Cow::Owned(value),
}
}
}
impl IntoResponse for JsonStr {
fn into_response(self) -> Response {
let mut res = self.inner.into_owned().into_response();
res.headers_mut().insert(
header::CONTENT_TYPE,
HeaderValue::from_static("application/json"),
);
res
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct RouteConf {
pub name: Cow<'static, str>,
pub methods: Methods,
pub path: Cow<'static, str>,
}
impl Default for RouteConf {
fn default() -> Self {
Self {
name: Cow::Borrowed(""),
methods: Methods::GET,
path: Cow::Borrowed("/"),
}
}
}