use chrono::{DateTime, Utc};
use chrono::format::{Item, Fixed, Numeric, Pad};
pub struct EtagsIter<'a>(&'a str);
impl<'a> EtagsIter<'a> {
pub fn new(value: &'a str) -> Self {
Self(value)
}
}
impl<'a> Iterator for EtagsIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.0 = self.0.trim_start();
if self.0.is_empty() {
return None
}
let prefix_len = if self.0.starts_with('"') {
1
}
else if self.0.starts_with("W/\"") {
3
}
else {
return None
};
let end = match self.0[prefix_len..].find('"') {
Some(index) => index + prefix_len + 1,
None => return None
};
let res = &self.0[0..end];
self.0 = self.0[end..].trim_start();
if self.0.starts_with(',') {
self.0 = self.0[1..].trim_start();
}
Some(res)
}
}
const IMF_FIXDATE: &[Item<'static>] = &[
Item::Space(""),
Item::Fixed(Fixed::ShortWeekdayName),
Item::Space(""),
Item::Literal(","),
Item::Space(" "),
Item::Numeric(Numeric::Day, Pad::Zero),
Item::Space(" "),
Item::Fixed(Fixed::ShortMonthName),
Item::Space(" "),
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(" "),
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Space(" "),
Item::Literal("GMT"),
Item::Space(""),
];
const RFC850_DATE: &[Item<'static>] = &[
Item::Space(""),
Item::Fixed(Fixed::LongWeekdayName),
Item::Space(""),
Item::Literal(","),
Item::Space(" "),
Item::Numeric(Numeric::Day, Pad::Zero),
Item::Literal("-"),
Item::Fixed(Fixed::ShortMonthName),
Item::Literal("-"),
Item::Numeric(Numeric::YearMod100, Pad::Zero),
Item::Space(" "),
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Space(" "),
Item::Literal("GMT"),
Item::Space(""),
];
const ASCTIME_DATE: &[Item<'static>] = &[
Item::Space(""),
Item::Fixed(Fixed::ShortWeekdayName),
Item::Space(" "),
Item::Fixed(Fixed::ShortMonthName),
Item::Space(" "),
Item::Numeric(Numeric::Day, Pad::Space),
Item::Space(" "),
Item::Numeric(Numeric::Hour, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Minute, Pad::Zero),
Item::Literal(":"),
Item::Numeric(Numeric::Second, Pad::Zero),
Item::Space(" "),
Item::Numeric(Numeric::Year, Pad::Zero),
Item::Space(""),
];
#[allow(clippy::question_mark)] pub fn parse_http_date(date: &str) -> Option<DateTime<Utc>> {
use chrono::format::{Parsed, parse};
let mut parsed = Parsed::new();
if parse(&mut parsed, date, IMF_FIXDATE.iter()).is_err() {
parsed = Parsed::new();
if parse(&mut parsed, date, RFC850_DATE.iter()).is_err() {
parsed = Parsed::new();
if parse(&mut parsed, date, ASCTIME_DATE.iter()).is_err() {
return None
}
}
}
parsed.to_datetime_with_timezone(&Utc).ok()
}
pub fn format_http_date(date: DateTime<Utc>) -> String {
date.format_with_items(IMF_FIXDATE.iter()).to_string()
}