rama_http_headers/common/
if_none_match.rs1use rama_core::telemetry::tracing;
2use rama_http_types::HeaderValue;
3use rama_utils::collections::NonEmptyVec;
4
5use super::ETag;
6use crate::util::{EntityTagRange, TryFromValues as _};
7
8#[derive(Clone, Debug, PartialEq)]
44pub struct IfNoneMatch(EntityTagRange);
45
46impl crate::TypedHeader for IfNoneMatch {
47 fn name() -> &'static ::rama_http_types::header::HeaderName {
48 &::rama_http_types::header::IF_NONE_MATCH
49 }
50}
51
52impl crate::HeaderDecode for IfNoneMatch {
53 fn decode<'i, I>(values: &mut I) -> Result<Self, crate::Error>
54 where
55 I: Iterator<Item = &'i ::rama_http_types::header::HeaderValue>,
56 {
57 EntityTagRange::try_from_values(values).map(Self)
58 }
59}
60
61impl crate::HeaderEncode for IfNoneMatch {
62 fn encode<E: Extend<::rama_http_types::HeaderValue>>(&self, values: &mut E) {
63 match HeaderValue::try_from(&self.0) {
64 Ok(value) => values.extend(::std::iter::once(value)),
65 Err(err) => {
66 tracing::debug!(
67 "failed to encode if-none-match entity-tag-range as header value: {err}"
68 );
69 }
70 }
71 }
72}
73
74impl IfNoneMatch {
75 pub fn any() -> Self {
77 Self(EntityTagRange::Any)
78 }
79
80 pub fn precondition_passes(&self, etag: &ETag) -> bool {
82 !self.0.matches_weak(&etag.0)
83 }
84}
85
86impl From<ETag> for IfNoneMatch {
87 fn from(etag: ETag) -> Self {
88 Self(EntityTagRange::Tags(NonEmptyVec::new(etag.0)))
89 }
90}
91
92#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn precondition_fails() {
108 let foo = ETag::from_static("\"foo\"");
109 let weak_foo = ETag::from_static("W/\"foo\"");
110
111 let if_none = IfNoneMatch::from(foo.clone());
112
113 assert!(!if_none.precondition_passes(&foo));
114 assert!(!if_none.precondition_passes(&weak_foo));
115 }
116
117 #[test]
118 fn precondition_passes() {
119 let if_none = IfNoneMatch::from(ETag::from_static("\"foo\""));
120
121 let bar = ETag::from_static("\"bar\"");
122 let weak_bar = ETag::from_static("W/\"bar\"");
123
124 assert!(if_none.precondition_passes(&bar));
125 assert!(if_none.precondition_passes(&weak_bar));
126 }
127
128 #[test]
129 fn precondition_any() {
130 let foo = ETag::from_static("\"foo\"");
131
132 let if_none = IfNoneMatch::any();
133
134 assert!(!if_none.precondition_passes(&foo));
135 }
136}