rust_serv/memory_cache/
cached_file.rs1use std::time::{Duration, Instant};
4use bytes::Bytes;
5
6#[derive(Clone, Debug)]
8pub struct CachedFile {
9 pub content: Bytes,
11 pub mime_type: String,
13 pub etag: String,
15 pub last_modified: u64,
17 pub created_at: Instant,
19 pub ttl: Duration,
21 pub size: usize,
23}
24
25impl CachedFile {
26 pub fn new(
28 content: Bytes,
29 mime_type: String,
30 etag: String,
31 last_modified: u64,
32 ttl: Duration,
33 ) -> Self {
34 let size = content.len();
35 Self {
36 content,
37 mime_type,
38 etag,
39 last_modified,
40 created_at: Instant::now(),
41 ttl,
42 size,
43 }
44 }
45
46 pub fn is_expired(&self) -> bool {
48 self.created_at.elapsed() > self.ttl
49 }
50
51 pub fn age(&self) -> Duration {
53 self.created_at.elapsed()
54 }
55
56 pub fn etag_matches(&self, etag: &str) -> bool {
58 self.etag == etag
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_cached_file_creation() {
68 let content = Bytes::from("test content");
69 let cached = CachedFile::new(
70 content.clone(),
71 "text/plain".to_string(),
72 "\"abc123\"".to_string(),
73 1234567890,
74 Duration::from_secs(300),
75 );
76
77 assert_eq!(cached.content, content);
78 assert_eq!(cached.mime_type, "text/plain");
79 assert_eq!(cached.etag, "\"abc123\"");
80 assert_eq!(cached.last_modified, 1234567890);
81 assert_eq!(cached.size, 12);
82 }
83
84 #[test]
85 fn test_cached_file_not_expired() {
86 let cached = CachedFile::new(
87 Bytes::from("test"),
88 "text/plain".to_string(),
89 "\"etag\"".to_string(),
90 0,
91 Duration::from_secs(300),
92 );
93
94 assert!(!cached.is_expired());
95 }
96
97 #[test]
98 fn test_cached_file_expired() {
99 let mut cached = CachedFile::new(
100 Bytes::from("test"),
101 "text/plain".to_string(),
102 "\"etag\"".to_string(),
103 0,
104 Duration::from_secs(0),
105 );
106 cached.created_at = Instant::now() - Duration::from_secs(1);
108
109 assert!(cached.is_expired());
110 }
111
112 #[test]
113 fn test_cached_file_age() {
114 let cached = CachedFile::new(
115 Bytes::from("test"),
116 "text/plain".to_string(),
117 "\"etag\"".to_string(),
118 0,
119 Duration::from_secs(300),
120 );
121
122 let age = cached.age();
123 assert!(age < Duration::from_millis(100));
124 }
125
126 #[test]
127 fn test_etag_matches() {
128 let cached = CachedFile::new(
129 Bytes::from("test"),
130 "text/plain".to_string(),
131 "\"abc123\"".to_string(),
132 0,
133 Duration::from_secs(300),
134 );
135
136 assert!(cached.etag_matches("\"abc123\""));
137 assert!(!cached.etag_matches("\"xyz789\""));
138 }
139
140 #[test]
141 fn test_cached_file_size() {
142 let content = Bytes::from("Hello, World!");
143 let cached = CachedFile::new(
144 content,
145 "text/plain".to_string(),
146 "\"etag\"".to_string(),
147 0,
148 Duration::from_secs(300),
149 );
150
151 assert_eq!(cached.size, 13);
152 }
153
154 #[test]
155 fn test_cached_file_clone() {
156 let cached = CachedFile::new(
157 Bytes::from("test"),
158 "text/plain".to_string(),
159 "\"etag\"".to_string(),
160 0,
161 Duration::from_secs(300),
162 );
163
164 let cloned = cached.clone();
165 assert_eq!(cached.content, cloned.content);
166 assert_eq!(cached.mime_type, cloned.mime_type);
167 assert_eq!(cached.etag, cloned.etag);
168 }
169
170 #[test]
171 fn test_empty_cached_file() {
172 let cached = CachedFile::new(
173 Bytes::new(),
174 "text/plain".to_string(),
175 "\"etag\"".to_string(),
176 0,
177 Duration::from_secs(300),
178 );
179
180 assert_eq!(cached.size, 0);
181 assert_eq!(cached.content.len(), 0);
182 }
183}