headers/common/etag.rs
1use std::str::FromStr;
2
3use crate::util::EntityTag;
4
5/// `ETag` header, defined in [RFC7232](https://datatracker.ietf.org/doc/html/rfc7232#section-2.3)
6///
7/// The `ETag` header field in a response provides the current entity-tag
8/// for the selected representation, as determined at the conclusion of
9/// handling the request. An entity-tag is an opaque validator for
10/// differentiating between multiple representations of the same
11/// resource, regardless of whether those multiple representations are
12/// due to resource state changes over time, content negotiation
13/// resulting in multiple representations being valid at the same time,
14/// or both. An entity-tag consists of an opaque quoted string, possibly
15/// prefixed by a weakness indicator.
16///
17/// # ABNF
18///
19/// ```text
20/// ETag = entity-tag
21/// ```
22///
23/// # Example values
24///
25/// * `"xyzzy"`
26/// * `W/"xyzzy"`
27/// * `""`
28///
29/// # Examples
30///
31/// ```
32/// let etag = "\"xyzzy\"".parse::<headers::ETag>().unwrap();
33/// ```
34#[derive(Clone, Debug, PartialEq, Eq)]
35pub struct ETag(pub(super) EntityTag);
36
37derive_header! {
38 ETag(_),
39 name: ETAG
40}
41
42impl ETag {
43 #[cfg(test)]
44 pub(crate) fn from_static(src: &'static str) -> ETag {
45 ETag(EntityTag::from_static(src))
46 }
47
48 /// Returns `true` if the etag is [weak](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag#directives).
49 pub fn is_weak(&self) -> bool {
50 self.0.is_weak()
51 }
52}
53
54error_type!(InvalidETag);
55
56impl FromStr for ETag {
57 type Err = InvalidETag;
58 fn from_str(src: &str) -> Result<Self, Self::Err> {
59 let val = src.parse().map_err(|_| InvalidETag { _inner: () })?;
60
61 EntityTag::from_owned(val)
62 .map(ETag)
63 .ok_or(InvalidETag { _inner: () })
64 }
65}
66
67/*
68test_etag {
69 // From the RFC
70 test_header!(test1,
71 vec![b"\"xyzzy\""],
72 Some(ETag(EntityTag::new(false, "xyzzy".to_owned()))));
73 test_header!(test2,
74 vec![b"W/\"xyzzy\""],
75 Some(ETag(EntityTag::new(true, "xyzzy".to_owned()))));
76 test_header!(test3,
77 vec![b"\"\""],
78 Some(ETag(EntityTag::new(false, "".to_owned()))));
79 // Own tests
80 test_header!(test4,
81 vec![b"\"foobar\""],
82 Some(ETag(EntityTag::new(false, "foobar".to_owned()))));
83 test_header!(test5,
84 vec![b"\"\""],
85 Some(ETag(EntityTag::new(false, "".to_owned()))));
86 test_header!(test6,
87 vec![b"W/\"weak-etag\""],
88 Some(ETag(EntityTag::new(true, "weak-etag".to_owned()))));
89 test_header!(test7,
90 vec![b"W/\"\x65\x62\""],
91 Some(ETag(EntityTag::new(true, "\u{0065}\u{0062}".to_owned()))));
92 test_header!(test8,
93 vec![b"W/\"\""],
94 Some(ETag(EntityTag::new(true, "".to_owned()))));
95 test_header!(test9,
96 vec![b"no-dquotes"],
97 None::<ETag>);
98 test_header!(test10,
99 vec![b"w/\"the-first-w-is-case-sensitive\""],
100 None::<ETag>);
101 test_header!(test11,
102 vec![b""],
103 None::<ETag>);
104 test_header!(test12,
105 vec![b"\"unmatched-dquotes1"],
106 None::<ETag>);
107 test_header!(test13,
108 vec![b"unmatched-dquotes2\""],
109 None::<ETag>);
110 test_header!(test14,
111 vec![b"matched-\"dquotes\""],
112 None::<ETag>);
113 test_header!(test15,
114 vec![b"\""],
115 None::<ETag>);
116}
117*/