rocket_include_static_resources/debug/
file_resources.rs1use std::{
2 collections::HashMap,
3 fs,
4 io::{self, ErrorKind},
5 path::PathBuf,
6 sync::Arc,
7 time::SystemTime,
8};
9
10use mime::Mime;
11
12use crate::{functions::compute_data_etag, mime, EntityTag};
13
14#[derive(Debug)]
15struct Resource {
16 path: PathBuf,
17 mime: Mime,
19 data: Arc<Vec<u8>>,
20 etag: EntityTag<'static>,
21 mtime: Option<SystemTime>,
22}
23
24#[derive(Debug)]
25pub struct FileResources {
27 resources: HashMap<&'static str, Resource>,
28}
29
30impl FileResources {
31 #[inline]
33 pub fn new() -> FileResources {
34 FileResources {
35 resources: HashMap::new()
36 }
37 }
38
39 #[inline]
41 pub fn register_resource_file<P: Into<PathBuf>>(
42 &mut self,
43 name: &'static str,
44 file_path: P,
45 ) -> Result<(), io::Error> {
46 let path = file_path.into();
47
48 let metadata = path.metadata()?;
49
50 let mtime = metadata.modified().ok();
51
52 let data = fs::read(&path)?;
53
54 let etag = compute_data_etag(&data);
55
56 let mime = match path.extension() {
57 Some(extension) => match extension.to_str() {
58 Some(extension) => mime_guess::from_ext(extension).first_or_octet_stream(),
59 None => mime::APPLICATION_OCTET_STREAM,
60 },
61 None => mime::APPLICATION_OCTET_STREAM,
62 };
63
64 let resource = Resource {
65 path,
66 mime,
67 data: Arc::new(data),
68 etag,
69 mtime,
70 };
71
72 self.resources.insert(name, resource);
73
74 Ok(())
75 }
76
77 #[inline]
79 pub fn unregister_resource_file<S: AsRef<str>>(&mut self, name: S) -> Option<PathBuf> {
80 let name = name.as_ref();
81
82 self.resources.remove(name).map(|resource| resource.path)
83 }
84
85 #[inline]
87 pub fn reload_if_needed(&mut self) -> Result<(), io::Error> {
88 for resource in self.resources.values_mut() {
89 let metadata = resource.path.metadata()?;
90
91 let (reload, new_mtime) = match resource.mtime {
92 Some(mtime) => match metadata.modified() {
93 Ok(new_mtime) => (new_mtime > mtime, Some(new_mtime)),
94 Err(_) => (true, None),
95 },
96 None => match metadata.modified() {
97 Ok(new_mtime) => (true, Some(new_mtime)),
98 Err(_) => (true, None),
99 },
100 };
101
102 if reload {
103 let new_data = fs::read(&resource.path)?;
104
105 let new_etag = compute_data_etag(&new_data);
106
107 resource.data = Arc::new(new_data);
108
109 resource.etag = new_etag;
110
111 resource.mtime = new_mtime;
112 }
113 }
114
115 Ok(())
116 }
117
118 #[allow(clippy::type_complexity)]
119 #[inline]
121 pub fn get_resource<S: AsRef<str>>(
122 &mut self,
123 name: S,
124 ) -> Result<(Mime, Arc<Vec<u8>>, &EntityTag<'static>), io::Error> {
125 let name = name.as_ref();
126
127 let resource = self.resources.get_mut(name).ok_or_else(|| {
128 io::Error::new(ErrorKind::NotFound, format!("The name `{}` is not found.", name))
129 })?;
130
131 let metadata = resource.path.metadata()?;
132
133 let (reload, new_mtime) = match resource.mtime {
134 Some(mtime) => match metadata.modified() {
135 Ok(new_mtime) => (new_mtime > mtime, Some(new_mtime)),
136 Err(_) => (true, None),
137 },
138 None => match metadata.modified() {
139 Ok(new_mtime) => (true, Some(new_mtime)),
140 Err(_) => (true, None),
141 },
142 };
143
144 if reload {
145 let new_data = fs::read(&resource.path)?;
146
147 let new_etag = compute_data_etag(&new_data);
148
149 resource.data = Arc::new(new_data);
150
151 resource.etag = new_etag;
152
153 resource.mtime = new_mtime;
154 }
155
156 Ok((resource.mime.clone(), resource.data.clone(), &resource.etag))
157 }
158}
159
160impl Default for FileResources {
161 #[inline]
162 fn default() -> Self {
163 FileResources::new()
164 }
165}