use axum::http::HeaderMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CountPreference {
Exact,
Planned,
Estimated,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum HandlingPreference {
Lenient,
Strict,
}
#[derive(Debug, Clone, Default)]
pub struct PreferHeader {
pub count_exact: bool,
pub count_planned: bool,
pub count_estimated: bool,
pub return_representation: bool,
pub return_minimal: bool,
pub resolution: Option<String>,
pub tx_rollback: bool,
pub handling: Option<HandlingPreference>,
pub max_affected: Option<u64>,
}
impl PreferHeader {
#[must_use]
pub const fn count_preference(&self) -> Option<CountPreference> {
if self.count_exact {
Some(CountPreference::Exact)
} else if self.count_planned {
Some(CountPreference::Planned)
} else if self.count_estimated {
Some(CountPreference::Estimated)
} else {
None
}
}
}
impl PreferHeader {
#[must_use]
pub fn parse(header_value: &str) -> Self {
let mut result = Self::default();
for pref in header_value.split(',') {
let pref = pref.trim();
if pref.eq_ignore_ascii_case("count=exact") {
result.count_exact = true;
result.count_planned = false;
result.count_estimated = false;
} else if pref.eq_ignore_ascii_case("count=planned") {
result.count_planned = true;
result.count_exact = false;
result.count_estimated = false;
} else if pref.eq_ignore_ascii_case("count=estimated") {
result.count_estimated = true;
result.count_exact = false;
result.count_planned = false;
} else if pref.eq_ignore_ascii_case("return=representation") {
result.return_representation = true;
result.return_minimal = false;
} else if pref.eq_ignore_ascii_case("return=minimal") {
result.return_minimal = true;
result.return_representation = false;
} else if pref.eq_ignore_ascii_case("tx=rollback") {
result.tx_rollback = true;
} else if pref.eq_ignore_ascii_case("tx=commit") {
result.tx_rollback = false;
} else if pref.eq_ignore_ascii_case("handling=strict") {
result.handling = Some(HandlingPreference::Strict);
} else if pref.eq_ignore_ascii_case("handling=lenient") {
result.handling = Some(HandlingPreference::Lenient);
} else if let Some(val) = strip_prefix_ci(pref, "resolution=") {
result.resolution = Some(val.to_string());
} else if let Some(val) = strip_prefix_ci(pref, "max-affected=") {
if let Ok(n) = val.parse::<u64>() {
result.max_affected = Some(n);
}
}
}
result
}
#[must_use]
pub fn from_headers(headers: &HeaderMap) -> Self {
let mut result = Self::default();
for value in headers.get_all("prefer") {
if let Ok(s) = value.to_str() {
let parsed = Self::parse(s);
if parsed.count_exact {
result.count_exact = true;
result.count_planned = false;
result.count_estimated = false;
} else if parsed.count_planned {
result.count_planned = true;
result.count_exact = false;
result.count_estimated = false;
} else if parsed.count_estimated {
result.count_estimated = true;
result.count_exact = false;
result.count_planned = false;
}
if parsed.return_representation {
result.return_representation = true;
result.return_minimal = false;
}
if parsed.return_minimal {
result.return_minimal = true;
result.return_representation = false;
}
if parsed.tx_rollback {
result.tx_rollback = true;
}
if parsed.handling.is_some() {
result.handling = parsed.handling;
}
if parsed.resolution.is_some() {
result.resolution = parsed.resolution;
}
if parsed.max_affected.is_some() {
result.max_affected = parsed.max_affected;
}
}
}
result
}
}
fn strip_prefix_ci<'a>(s: &'a str, prefix: &str) -> Option<&'a str> {
if s.len() >= prefix.len() && s[..prefix.len()].eq_ignore_ascii_case(prefix) {
Some(&s[prefix.len()..])
} else {
None
}
}