lychee_lib/types/
cache.rs1use std::fmt::Display;
2
3use serde::{Deserialize, Deserializer, Serialize};
4
5use crate::{ErrorKind, Status, StatusCodeExcluder};
6
7#[derive(Debug, Serialize, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
11pub enum CacheStatus {
12 Ok(u16),
14 Error(Option<u16>),
16 Excluded,
18 Unsupported,
25}
26
27impl<'de> Deserialize<'de> for CacheStatus {
28 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
29 where
30 D: Deserializer<'de>,
31 {
32 let status = <&str as Deserialize<'de>>::deserialize(deserializer)?;
33 match status {
34 "Excluded" => Ok(CacheStatus::Excluded),
35 "Unsupported" => Ok(CacheStatus::Unsupported),
39 other => match other.parse::<u16>() {
40 Ok(code) => match code {
41 200..=299 => Ok(CacheStatus::Ok(code)),
46 _ => Ok(CacheStatus::Error(Some(code))),
48 },
49 Err(_) => Ok(CacheStatus::Error(None)),
50 },
51 }
52 }
53}
54
55impl Display for CacheStatus {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Self::Ok(_) => write!(f, "OK (cached)"),
59 Self::Error(_) => write!(f, "Error (cached)"),
60 Self::Excluded => write!(f, "Excluded (cached)"),
61 Self::Unsupported => write!(f, "Unsupported (cached)"),
62 }
63 }
64}
65
66impl From<&Status> for CacheStatus {
67 fn from(s: &Status) -> Self {
68 match s {
69 Status::Cached(s) => *s,
70 Status::Ok(code) | Status::UnknownStatusCode(code) => Self::Ok(code.as_u16()),
74 Status::Excluded => Self::Excluded,
75 Status::Unsupported(_) => Self::Unsupported,
76 Status::Redirected(code, _) => Self::Error(Some(code.as_u16())),
77 Status::Timeout(code) => Self::Error(code.map(|code| code.as_u16())),
78 Status::Error(e) => match e {
79 ErrorKind::RejectedStatusCode(code) => Self::Error(Some(code.as_u16())),
80 ErrorKind::ReadResponseBody(e) | ErrorKind::BuildRequestClient(e) => {
81 match e.status() {
82 Some(code) => Self::Error(Some(code.as_u16())),
83 None => Self::Error(None),
84 }
85 }
86 _ => Self::Error(None),
87 },
88 Status::RequestError(_) => Self::Error(None),
89 }
90 }
91}
92
93impl From<CacheStatus> for Option<u16> {
94 fn from(val: CacheStatus) -> Self {
95 match val {
96 CacheStatus::Ok(status) => Some(status),
97 CacheStatus::Error(status) => status,
98 _ => None,
99 }
100 }
101}
102
103impl CacheStatus {
104 #[must_use]
106 pub fn is_excluded(&self, excluder: &StatusCodeExcluder) -> bool {
107 match Option::<u16>::from(*self) {
108 Some(status) => excluder.contains(status),
109 _ => false,
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use serde::Deserialize;
117 use serde::de::value::{BorrowedStrDeserializer, Error as DeserializerError};
118
119 use crate::CacheStatus;
120
121 fn deserialize_cache_status(s: &str) -> Result<CacheStatus, DeserializerError> {
122 let deserializer: BorrowedStrDeserializer<DeserializerError> =
123 BorrowedStrDeserializer::new(s);
124 CacheStatus::deserialize(deserializer)
125 }
126
127 #[test]
128 fn test_deserialize_cache_status_success_code() {
129 assert_eq!(deserialize_cache_status("200"), Ok(CacheStatus::Ok(200)));
130 }
131
132 #[test]
133 fn test_deserialize_cache_status_error_code() {
134 assert_eq!(
135 deserialize_cache_status("404"),
136 Ok(CacheStatus::Error(Some(404)))
137 );
138 }
139
140 #[test]
141 fn test_deserialize_cache_status_excluded() {
142 assert_eq!(
143 deserialize_cache_status("Excluded"),
144 Ok(CacheStatus::Excluded)
145 );
146 }
147
148 #[test]
149 fn test_deserialize_cache_status_unsupported() {
150 assert_eq!(
151 deserialize_cache_status("Unsupported"),
152 Ok(CacheStatus::Unsupported)
153 );
154 }
155
156 #[test]
157 fn test_deserialize_cache_status_blank() {
158 assert_eq!(deserialize_cache_status(""), Ok(CacheStatus::Error(None)));
159 }
160}