kutil_http/headers/
etag.rs

1use super::{super::cache::*, preferences::*};
2
3use {
4    bytestring::*,
5    kutil_std::string::*,
6    std::{convert::*, fmt, str::*},
7};
8
9//
10// ETag
11//
12
13/// ETag value in HTTP headers.
14#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub struct ETag {
16    /// Tag.
17    pub tag: ByteString,
18
19    /// Weak.
20    pub weak: bool,
21}
22
23impl ETag {
24    /// Constructor.
25    pub fn new(tag: ByteString, weak: bool) -> Self {
26        Self { tag, weak }
27    }
28
29    /// Parse list.
30    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//
80// ETagList
81//
82
83/// List of [ETag].
84#[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//
97// ETagMatcher
98//
99
100/// [ETag] matcher.
101#[derive(Clone, Debug, Eq, Hash, PartialEq)]
102pub struct ETagMatcher(pub Selector<ETagList>);
103
104impl ETagMatcher {
105    /// Whether any one of our tags matches the reference.
106    ///
107    /// [Any](Selector::Any) will always match. Weak tags will *never* match.
108    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}