use std::fmt;
use std::str::FromStr;
use axum::http::HeaderMap;
pub const PRIORITY_HEADER: &str = "x-priority";
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum RequestPriority {
High = 1,
#[default]
Normal = 2,
Low = 3,
Background = 4,
}
impl RequestPriority {
#[must_use]
pub fn as_u8(&self) -> u8 {
*self as u8
}
#[must_use]
pub fn from_header_value(value: &str) -> Self {
let normalized = value.trim().to_lowercase();
if let Ok(num) = normalized.parse::<u8>() {
return match num {
1 => Self::High,
2 => Self::Normal,
3 => Self::Low,
4 => Self::Background,
_ => Self::Normal,
};
}
match normalized.as_str() {
"high" | "critical" | "urgent" => Self::High,
"normal" | "default" | "standard" => Self::Normal,
"low" | "batch" => Self::Low,
"background" | "bg" | "idle" => Self::Background,
_ => Self::Normal,
}
}
#[must_use]
pub fn from_headers(headers: &HeaderMap) -> Self {
headers
.get(PRIORITY_HEADER)
.and_then(|v| v.to_str().ok())
.map(Self::from_header_value)
.unwrap_or_default()
}
#[must_use]
pub fn is_higher_than(&self, other: &Self) -> bool {
self.as_u8() < other.as_u8()
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::High => "high",
Self::Normal => "normal",
Self::Low => "low",
Self::Background => "background",
}
}
}
impl fmt::Display for RequestPriority {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl FromStr for RequestPriority {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from_header_value(s))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_priority_default() {
assert_eq!(RequestPriority::default(), RequestPriority::Normal);
}
#[test]
fn test_priority_ordering() {
assert!(RequestPriority::High < RequestPriority::Normal);
assert!(RequestPriority::Normal < RequestPriority::Low);
assert!(RequestPriority::Low < RequestPriority::Background);
}
#[test]
fn test_priority_from_numeric() {
assert_eq!(
RequestPriority::from_header_value("1"),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value("2"),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value("3"),
RequestPriority::Low
);
assert_eq!(
RequestPriority::from_header_value("4"),
RequestPriority::Background
);
assert_eq!(
RequestPriority::from_header_value("5"),
RequestPriority::Normal
); assert_eq!(
RequestPriority::from_header_value("0"),
RequestPriority::Normal
); }
#[test]
fn test_priority_from_string() {
assert_eq!(
RequestPriority::from_header_value("high"),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value("HIGH"),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value("critical"),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value("urgent"),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value("normal"),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value("default"),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value("standard"),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value("low"),
RequestPriority::Low
);
assert_eq!(
RequestPriority::from_header_value("batch"),
RequestPriority::Low
);
assert_eq!(
RequestPriority::from_header_value("background"),
RequestPriority::Background
);
assert_eq!(
RequestPriority::from_header_value("bg"),
RequestPriority::Background
);
assert_eq!(
RequestPriority::from_header_value("idle"),
RequestPriority::Background
);
}
#[test]
fn test_priority_from_invalid() {
assert_eq!(
RequestPriority::from_header_value("invalid"),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value(""),
RequestPriority::Normal
);
assert_eq!(
RequestPriority::from_header_value(" "),
RequestPriority::Normal
);
}
#[test]
fn test_priority_as_u8() {
assert_eq!(RequestPriority::High.as_u8(), 1);
assert_eq!(RequestPriority::Normal.as_u8(), 2);
assert_eq!(RequestPriority::Low.as_u8(), 3);
assert_eq!(RequestPriority::Background.as_u8(), 4);
}
#[test]
fn test_priority_display() {
assert_eq!(RequestPriority::High.to_string(), "high");
assert_eq!(RequestPriority::Normal.to_string(), "normal");
assert_eq!(RequestPriority::Low.to_string(), "low");
assert_eq!(RequestPriority::Background.to_string(), "background");
}
#[test]
fn test_priority_is_higher_than() {
assert!(RequestPriority::High.is_higher_than(&RequestPriority::Normal));
assert!(RequestPriority::High.is_higher_than(&RequestPriority::Low));
assert!(RequestPriority::Normal.is_higher_than(&RequestPriority::Low));
assert!(!RequestPriority::Low.is_higher_than(&RequestPriority::Normal));
assert!(!RequestPriority::Normal.is_higher_than(&RequestPriority::High));
}
#[test]
fn test_priority_from_str() {
let priority: RequestPriority = "high".parse().expect("parse should succeed");
assert_eq!(priority, RequestPriority::High);
let priority: RequestPriority = "3".parse().expect("parse should succeed");
assert_eq!(priority, RequestPriority::Low);
}
#[test]
fn test_priority_whitespace_handling() {
assert_eq!(
RequestPriority::from_header_value(" high "),
RequestPriority::High
);
assert_eq!(
RequestPriority::from_header_value(" 1 "),
RequestPriority::High
);
}
#[test]
fn test_priority_from_headers() {
let mut headers = HeaderMap::new();
headers.insert(PRIORITY_HEADER, "high".parse().expect("valid header value"));
assert_eq!(
RequestPriority::from_headers(&headers),
RequestPriority::High
);
headers.insert(PRIORITY_HEADER, "3".parse().expect("valid header value"));
assert_eq!(
RequestPriority::from_headers(&headers),
RequestPriority::Low
);
let empty_headers = HeaderMap::new();
assert_eq!(
RequestPriority::from_headers(&empty_headers),
RequestPriority::Normal
);
}
}