use crate::date::{DateError, HttpDate};
use crate::etag::{ETagList, EntityTag, parse_etag_list};
use crate::validate::trim_ows;
use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConditionalError {
Empty,
InvalidFormat,
ETagError,
DateError,
}
impl fmt::Display for ConditionalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConditionalError::Empty => write!(f, "empty conditional header"),
ConditionalError::InvalidFormat => write!(f, "invalid conditional header format"),
ConditionalError::ETagError => write!(f, "invalid etag in conditional header"),
ConditionalError::DateError => write!(f, "invalid date in conditional header"),
}
}
}
impl core::error::Error for ConditionalError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfMatch(ETagList);
impl IfMatch {
pub fn parse(input: &str) -> Result<Self, ConditionalError> {
parse_etag_list(input)
.map(IfMatch)
.map_err(|_| ConditionalError::ETagError)
}
pub fn is_any(&self) -> bool {
self.0.is_any()
}
pub fn matches(&self, etag: &EntityTag) -> bool {
self.0.contains_strong(etag)
}
pub fn etags(&self) -> &ETagList {
&self.0
}
}
impl fmt::Display for IfMatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfNoneMatch(ETagList);
impl IfNoneMatch {
pub fn parse(input: &str) -> Result<Self, ConditionalError> {
parse_etag_list(input)
.map(IfNoneMatch)
.map_err(|_| ConditionalError::ETagError)
}
pub fn is_any(&self) -> bool {
self.0.is_any()
}
pub fn matches(&self, etag: &EntityTag) -> bool {
!self.0.contains_weak(etag)
}
pub fn etags(&self) -> &ETagList {
&self.0
}
}
impl fmt::Display for IfNoneMatch {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfModifiedSince(HttpDate);
impl IfModifiedSince {
pub fn parse(input: &str, reference_year: u16) -> Result<Self, ConditionalError> {
HttpDate::parse(input)
.or_else(|e| match e {
DateError::Rfc850Date => HttpDate::parse_rfc850(input, reference_year),
other => Err(other),
})
.map(IfModifiedSince)
.map_err(|_| ConditionalError::DateError)
}
pub fn date(&self) -> &HttpDate {
&self.0
}
pub fn is_modified(&self, last_modified: &HttpDate) -> bool {
last_modified > &self.0
}
}
impl fmt::Display for IfModifiedSince {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfUnmodifiedSince(HttpDate);
impl IfUnmodifiedSince {
pub fn parse(input: &str, reference_year: u16) -> Result<Self, ConditionalError> {
HttpDate::parse(input)
.or_else(|e| match e {
DateError::Rfc850Date => HttpDate::parse_rfc850(input, reference_year),
other => Err(other),
})
.map(IfUnmodifiedSince)
.map_err(|_| ConditionalError::DateError)
}
pub fn date(&self) -> &HttpDate {
&self.0
}
}
impl fmt::Display for IfUnmodifiedSince {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IfRange {
ETag(EntityTag),
Date(HttpDate),
}
impl IfRange {
pub fn parse(input: &str, reference_year: u16) -> Result<Self, ConditionalError> {
let input = trim_ows(input);
if input.is_empty() {
return Err(ConditionalError::Empty);
}
if input.starts_with('"') || input.starts_with("W/") || input.starts_with("w/") {
EntityTag::parse(input)
.map(IfRange::ETag)
.map_err(|_| ConditionalError::ETagError)
} else {
HttpDate::parse(input)
.or_else(|e| match e {
DateError::Rfc850Date => HttpDate::parse_rfc850(input, reference_year),
other => Err(other),
})
.map(IfRange::Date)
.map_err(|_| ConditionalError::DateError)
}
}
pub fn is_etag(&self) -> bool {
matches!(self, IfRange::ETag(_))
}
pub fn is_date(&self) -> bool {
matches!(self, IfRange::Date(_))
}
pub fn etag(&self) -> Option<&EntityTag> {
match self {
IfRange::ETag(e) => Some(e),
IfRange::Date(_) => None,
}
}
pub fn date(&self) -> Option<&HttpDate> {
match self {
IfRange::ETag(_) => None,
IfRange::Date(d) => Some(d),
}
}
}
impl fmt::Display for IfRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IfRange::ETag(e) => write!(f, "{}", e),
IfRange::Date(d) => write!(f, "{}", d),
}
}
}