kutil_http/headers/
etag.rs1use super::{super::cache::*, preferences::*};
2
3use {
4 bytestring::*,
5 kutil_std::string::*,
6 std::{convert::*, fmt, str::*},
7};
8
9#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub struct ETag {
16 pub tag: ByteString,
18
19 pub weak: bool,
21}
22
23impl ETag {
24 pub fn new(tag: ByteString, weak: bool) -> Self {
26 Self { tag, weak }
27 }
28
29 pub fn parse_list(representation: &str) -> Option<Vec<Self>> {
31 let tags: Vec<_> = representation.split(",").map(|tag| tag.parse()).flatten().collect();
32 if !tags.is_empty() { Some(tags) } else { None }
33 }
34}
35
36impl CacheWeight for ETag {
37 fn cache_weight(&self) -> usize {
38 const SELF_SIZE: usize = size_of::<ETag>();
39 SELF_SIZE + self.tag.len()
40 }
41}
42
43impl FromStr for ETag {
44 type Err = ParseError;
45
46 fn from_str(representation: &str) -> Result<Self, Self::Err> {
47 let mut tag = representation.trim();
48
49 if tag.ends_with("\"") {
50 tag = &tag[..tag.len() - 1];
51 } else {
52 return Err("missing end '\"'".into());
53 }
54
55 let weak = if tag.starts_with("W/\"") {
56 tag = &tag[3..];
57 true
58 } else if tag.starts_with("\"") {
59 tag = &tag[1..];
60 false
61 } else {
62 return Err("missing start '\"'".into());
63 };
64
65 if tag.contains("\"") {
66 return Err("contains '\"'".into());
67 }
68
69 Ok(Self::new(tag.into(), weak))
70 }
71}
72
73impl fmt::Display for ETag {
74 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
75 if self.weak { write!(formatter, "W/\"{}\"", self.tag) } else { write!(formatter, "\"{}\"", self.tag) }
76 }
77}
78
79#[derive(Clone, Debug, Eq, Hash, PartialEq)]
85pub struct ETagList(pub Vec<ETag>);
86
87impl FromStr for ETagList {
88 type Err = ParseError;
89
90 fn from_str(representation: &str) -> Result<Self, Self::Err> {
91 let tags: Vec<_> = representation.split(",").map(|tag| tag.parse()).flatten().collect();
92 if !tags.is_empty() { Ok(Self(tags)) } else { Err("no tags".into()) }
93 }
94}
95
96#[derive(Clone, Debug, Eq, Hash, PartialEq)]
102pub struct ETagMatcher(pub Selector<ETagList>);
103
104impl ETagMatcher {
105 pub fn matches(&self, reference: Option<&ETag>) -> bool {
109 return match &self.0 {
110 Selector::Any => true,
111
112 Selector::Specific(selector) => {
113 if let Some(reference) = reference {
114 if !reference.weak && selector.0.contains(&reference) {
115 return true;
116 }
117 }
118
119 false
120 }
121 };
122 }
123}
124
125impl FromStr for ETagMatcher {
126 type Err = ParseError;
127
128 fn from_str(representation: &str) -> Result<Self, Self::Err> {
129 Ok(Self(representation.parse()?))
130 }
131}