1
2
3
4
5use ::std::{
6
7 io,
8 sync,
9 time,
10
11 collections::HashSet,
12
13 io::Write as _,
14
15 };
16
17
18use ::cpio::newc as cpio;
19
20
21use crate::{
22
23 StaticRouteDebug,
24
25 };
26
27
28use crate::hss::{
29
30 BodyExt as _,
31 ResponseExt as _,
32
33 ResultExtWrap as _,
34
35 fail_with_format,
36
37 };
38
39
40use crate::hss;
41
42
43
44
45pub fn export_routes_debug (_routes : impl Into<hss::Routes>, _output : impl io::Write) -> hss::ServerResult {
46
47 let mut _output = _output;
48
49 let mut _consumer = |_route : &hss::Route, _content_type : hss::ContentType, _content_buffer : Vec<u8>| {
50
51 if let Some (_debug) = _route.extensions.get::<StaticRouteDebug> () {
52 write! (_output, "** {} -> {:?}\n", _route.path, _debug) .or_wrap (0x99260590) ?;
53 } else {
54 write! (_output, "** {}\n", _route.path) .or_wrap (0xb7ff2169) ?;
55 }
56
57 write! (_output, ">> {} bytes of type `{}`;\n", _content_buffer.len (), _content_type.to_str ()) .or_wrap (0xaea1edf5) ?;
58
59 Ok (())
60 };
61
62 return export_routes_all (_routes, &mut _consumer);
63}
64
65
66
67
68pub fn export_routes_dump (_routes : impl Into<hss::Routes>, _route_path : &str, _output : impl io::Write) -> hss::ServerResult {
69
70 let mut _output = _output;
71
72 let mut _consumer = |_route : &hss::Route, _content_type : hss::ContentType, _content_buffer : Vec<u8>| {
73 _output.write_all (&_content_buffer) .or_wrap (0x2a238b7f)
74 };
75
76 return export_routes_one (_routes, _route_path, &mut _consumer);
77}
78
79
80
81
82pub fn export_routes_cpio (_routes : impl Into<hss::Routes>, _output : impl io::Write) -> hss::ServerResult {
83
84 let mut _output = _output;
85
86 let _timestamp = time::SystemTime::now () .duration_since (time::UNIX_EPOCH) .or_wrap (0x9c419037) ?;
87 let _timestamp : u32 = _timestamp.as_secs () .try_into () .or_wrap (0x451e1667) ?;
88
89 let mut _paths_seen = HashSet::new ();
90 let mut _folders = HashSet::new ();
91
92 let mut _consumer = |_route : &hss::Route, _content_type : hss::ContentType, _content_buffer : Vec<u8>| {
93
94 let _path = export_routes_cpio_path (_route, _content_type) ?;
95 let _content_size : u32 = _content_buffer.len () .try_into () .or_wrap (0xc3c8d6c2) ?;
96
97 if _paths_seen.contains (&_path) {
98 eprintln! ("[ww] [94617879] duplicate path encountered `{}`; ignoring!", _path);
99 }
100 _paths_seen.insert (_path.clone ());
101
102 {
103 let mut _folder = String::new ();
104 for _path_component in _path.split ('/') {
105 if ! _folder.is_empty () {
106 _folder.push ('/');
107 }
108 _folder.push_str (_path_component);
109 if _folder.len () == _path.len () {
110 break;
111 }
112 if _folders.contains (&_folder) {
113 continue;
114 }
115 if _paths_seen.contains (&_folder) {
116 eprintln! ("[ww] [fa649895] duplicate path encountered `{}`; ignoring!", _folder);
117 }
118 _folders.insert (_folder.clone ());
119 _paths_seen.insert (_folder.clone ());
120 cpio::Builder::new (&_folder)
121 .mode (0o755 | 0o040000)
122 .mtime (_timestamp)
123 .write (&mut _output, 0)
124 .finish () .or_wrap (0x0bfd8f69) ?
125 ;
126 }
127 }
128
129 let _cpio_entry = cpio::Builder::new (&_path)
130 .mode (0o644 | 0o100000)
131 .mtime (_timestamp)
132 ;
133 let mut _cpio_entry = _cpio_entry.write (&mut _output, _content_size);
134 _cpio_entry.write_all (&_content_buffer) .or_wrap (0x29b49136) ?;
135 _cpio_entry.finish () .or_wrap (0x76be56bc) ?;
136
137 Ok (())
138 };
139
140 export_routes_all (_routes, &mut _consumer) ?;
141
142 cpio::trailer (&mut _output) .or_wrap (0x4975bcae) ?;
143
144 return Ok (());
145}
146
147
148
149
150fn export_routes_cpio_path (_route : &hss::Route, _content_type : hss::ContentType) -> hss::ServerResult<String> {
151
152 if ! _route.path.starts_with ("/") || _route.path.is_empty () {
153 fail_with_format! (0xea316ecf, "failed resolving path for `{}` (missing `/` prefix)!", _route.path);
154 }
155 let mut _route_path = _route.path[1..].to_owned ();
156
157 if _route_path.ends_with ("/") || _route_path.is_empty () {
158 match _content_type {
159 hss::ContentType::Text =>
160 _route_path.push_str ("index.txt"),
161 hss::ContentType::Html =>
162 _route_path.push_str ("index.html"),
163 hss::ContentType::Xml =>
164 _route_path.push_str ("index.xml"),
165 hss::ContentType::Json =>
166 _route_path.push_str ("index.json"),
167 _ =>
168 _route_path.push_str ("index.data"),
169 }
170 }
171
172 let _extensions : &[&str] = match _content_type {
173 hss::ContentType::Text =>
174 &[".txt", ".text", ".md"],
175 hss::ContentType::Html =>
176 &[".html", ".htm", ".xhtml"],
177 hss::ContentType::Xml =>
178 &[".xml", ".xhtml"],
179 hss::ContentType::Json =>
180 &[".json"],
181 _ =>
182 &[],
183 };
184
185 if ! _extensions.is_empty () {
186 let mut _has_extension = false;
187 for _extension in _extensions {
188 if _route_path.ends_with (_extension) {
189 _has_extension = true;
190 break;
191 }
192 }
193 if ! _has_extension {
194 _route_path.push_str (_extensions[0]);
195 }
196 }
197
198 return Ok (_route_path);
199}
200
201
202
203
204pub fn export_routes_all <Consumer> (_routes : impl Into<hss::Routes>, _consumer : Consumer) -> hss::ServerResult
205 where
206 Consumer : FnMut (&hss::Route, hss::ContentType, Vec<u8>) -> hss::ServerResult,
207{
208 let _routes = _routes.into ();
209 let _runtime = hss::runtime_current_thread () ?;
210
211 let mut _consumer = _consumer;
212 for _route in _routes.routes () {
213 export_route_resolve (&_routes, &_route.path, &mut _consumer, &_runtime) ?;
214 }
215
216 return Ok (());
217}
218
219
220
221
222pub fn export_routes_one <Consumer> (_routes : impl Into<hss::Routes>, _route_path : &str, _consumer : Consumer) -> hss::ServerResult
223 where
224 Consumer : FnMut (&hss::Route, hss::ContentType, Vec<u8>) -> hss::ServerResult,
225{
226 let _routes = _routes.into ();
227 let _runtime = hss::runtime_current_thread () ?;
228
229 export_route_resolve (&_routes, _route_path, _consumer, &_runtime) ?;
230
231 return Ok (());
232}
233
234
235
236
237pub fn export_route_resolve <Consumer> (_routes : &hss::Routes, _route_path : &str, _consumer : Consumer, _runtime : &hss::Runtime) -> hss::ServerResult
238 where
239 Consumer : FnOnce (&hss::Route, hss::ContentType, Vec<u8>) -> hss::ServerResult,
240{
241 let _route_matched = match _routes.resolve (_route_path) {
242 Ok (Some (_route_matched)) =>
243 _route_matched,
244 Ok (None) =>
245 fail_with_format! (0xa712904b, "failed resolving route for `{}` (resolution failed)!", _route_path),
246 Err (_error) =>
247 fail_with_format! (0xea0e6963, "failed resolving route for `{}` (resolution failed)! // {}", _route_path, _error),
248 };
249
250 return export_route_matched (_route_matched, _consumer, _runtime);
251}
252
253
254
255
256pub fn export_route_matched <Consumer> (_route_matched : hss::RouteMatched, _consumer : Consumer, _runtime : &hss::Runtime) -> hss::ServerResult
257 where
258 Consumer : FnOnce (&hss::Route, hss::ContentType, Vec<u8>) -> hss::ServerResult,
259{
260 let _route = _route_matched.route.clone ();
261
262 let (_content_type, _content_buffer) = export_route_matched_0 (_route_matched, &_runtime) ?;
263
264 _consumer (&_route, _content_type, _content_buffer) ?;
265
266 return Ok (());
267}
268
269
270
271
272pub fn export_route_matched_0 (_route_matched : hss::RouteMatched, _runtime : &hss::Runtime) -> hss::ServerResult<(hss::ContentType, Vec<u8>)> {
273
274 let _route = sync::Arc::clone (&_route_matched.route);
275
276 let _request = hss::Request::get (_route.path.clone ()) .body (hss::Body::default ()) .or_wrap (0x5fbdc50e) ?;
277
278 let _future = _route.handle (_request, _route_matched);
279
280 let _response = match _runtime.block_on (_future) {
281 Ok (_response) =>
282 _response,
283 Err (_error) =>
284 fail_with_format! (0x349294f0, "failed generating response for `{}` (handling failed)! // {}", _route.path, _error),
285 };
286
287 let _status = _response.status ();
288 let _content_type = _response.content_type_or_unknown ();
289
290 let _body = match _status {
291 hss::consts::OK =>
292 _response.into_body (),
293 _ =>
294 fail_with_format! (0xbb5e6327, "failed generating response for `{}` (status {})!", _route.path, _status),
295 };
296
297 let _buffer = match export_body (_body, _runtime) {
298 Ok (_buffer) =>
299 _buffer,
300 Err (_error) =>
301 fail_with_format! (0x622b887a, "failed generating response for `{}` (streaming failed)! // {}", _route.path, _error),
302 };
303
304 return Ok ((_content_type, _buffer));
305}
306
307
308
309
310fn export_body (_body : hss::BodyDynBox, _runtime : &hss::Runtime) -> hss::ServerResult<Vec<u8>> {
311
312 let mut _body = _body;
313 return _body.consume_to_vec (Some (_runtime));
314}
315