use headers::{Header, HeaderName};
use tracing::error;
use vec1::smallvec_v1::SmallVec1;
use crate::field::rules::flat_csv::{Comma, FlatCsv};
mod preference;
pub use preference::*;
#[derive(Debug, Clone)]
pub struct Prefer {
pub preferences: SmallVec1<[Preference; 1]>,
}
pub static PREFER: HeaderName = HeaderName::from_static("prefer");
impl Header for Prefer {
#[inline]
fn name() -> &'static HeaderName {
&PREFER
}
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
where
Self: Sized,
I: Iterator<Item = &'i headers::HeaderValue>,
{
Ok(Prefer {
preferences: SmallVec1::try_from_smallvec(
values
.flat_map(|value| FlatCsv::<Comma>::from(value).iter())
.map(|value_str| value_str.parse())
.collect::<Result<_, _>>()
.map_err(|_| headers::Error::invalid())?,
)
.map_err(|_| {
error!("Zero number of preferences not allowed");
headers::Error::invalid()
})?,
})
}
fn encode<E: Extend<headers::HeaderValue>>(&self, values: &mut E) {
values.extend(self.preferences.iter().map(|preference| {
preference
.str_encode()
.parse()
.expect("Preference is always a valid HeaderValue")
}));
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use claims::*;
use headers::HeaderValue;
use preference::tests_decode::assert_valid_preference;
use rstest::*;
use super::*;
#[rstest]
#[case("Lenient, abc/def; a=1")]
#[case("abc def; b=2; Lenient")]
#[case("abc = def/pqr; b=2")]
#[case("abc = def pqr; b=2")]
#[case::invalid_param_key("pref1; abc; def/ghi = 123")]
fn header_with_invalid_preferences_will_be_rejected(#[case] header_value_str: &str) {
let header_value = assert_ok!(HeaderValue::from_str(header_value_str));
assert_err!(Prefer::decode(&mut std::iter::once(&header_value)));
}
fn assert_correspondence<'a>(
header_value_strs: impl Iterator<Item = &'a str>,
prefer: &Prefer,
) {
let header_values = header_value_strs.collect::<Vec<_>>();
assert_eq!(
prefer.preferences.len(),
header_values.len(),
"Mismatched length"
);
for (i, preference) in prefer.preferences.iter().enumerate() {
let preference2 = assert_ok!(Preference::from_str(header_values[i]));
assert_eq!(preference, &preference2, "Mismatched preference");
}
}
#[rstest]
#[case(&["foo; bar", "return=minimal; foo=\"some parameter\""])]
#[case(&[r#"return=representation; include="http://www.w3.org/ns/ldp#PreferMembership http://www.w3.org/ns/ldp#PreferMinimalContainer""#,"respond-async; wait=100"])]
fn valid_headers_will_be_decoded_correctly(#[case] header_value_strs: &[&str]) {
let header_values: Vec<HeaderValue> = header_value_strs
.iter()
.map(|v| assert_ok!(HeaderValue::from_str(v)))
.collect();
let prefer = assert_ok!(Prefer::decode(&mut header_values.iter()));
assert_correspondence(header_value_strs.iter().copied(), &prefer);
}
#[rstest]
#[case(&["foo; bar", "return=minimal; foo=\"some parameter\""])]
#[case(&[r#"return=representation; include="http://www.w3.org/ns/ldp#PreferMembership http://www.w3.org/ns/ldp#PreferMinimalContainer""#,"respond-async; wait=100"])]
fn encode_works_correctly(#[case] preference_strs: &[&str]) {
let prefer = Prefer {
preferences: assert_ok!(SmallVec1::try_from_smallvec(
preference_strs
.iter()
.map(|s| assert_valid_preference(s))
.collect()
)),
};
let mut header_values = Vec::new();
prefer.encode(&mut header_values);
assert_correspondence(header_values.iter().map(|v| v.to_str().unwrap()), &prefer);
}
}