1
1
//! rustdoc handler
2
2
3
- use crate :: {
4
- db:: Pool ,
5
- impl_webpage, utils,
6
- web:: {
7
- crate_details:: CrateDetails , error:: Nope , file:: File , match_version, metrics,
8
- page:: WebPage , redirect_base, MatchSemver ,
9
- } ,
10
- Config ,
11
- } ;
12
- use iron:: {
13
- headers:: { CacheControl , CacheDirective , Expires , HttpDate } ,
14
- modifiers:: Redirect ,
15
- status, Handler , IronError , IronResult , Plugin , Request , Response , Url ,
16
- } ;
3
+ use super :: crate_details:: CrateDetails ;
4
+ use super :: error:: Nope ;
5
+ use super :: file:: File ;
6
+ use super :: metrics;
7
+ use super :: page:: Page ;
8
+ use super :: redirect_base;
9
+ use super :: { match_version, MatchSemver } ;
10
+ use crate :: db:: Pool ;
11
+ use crate :: utils;
12
+ use crate :: Config ;
13
+ use iron:: headers:: { CacheControl , CacheDirective , Expires , HttpDate } ;
14
+ use iron:: modifiers:: Redirect ;
15
+ use iron:: prelude:: * ;
16
+ use iron:: Handler ;
17
+ use iron:: { status, Url } ;
17
18
use postgres:: Connection ;
18
19
use router:: Router ;
19
- use serde:: Serialize ;
20
+ use serde:: ser:: { Serialize , SerializeStruct , Serializer } ;
21
+
22
+ #[ derive( Debug , Default ) ]
23
+ struct RustdocPage {
24
+ head : String ,
25
+ body : String ,
26
+ body_class : String ,
27
+ name : String ,
28
+ full : String ,
29
+ version : String ,
30
+ description : Option < String > ,
31
+ crate_details : Option < CrateDetails > ,
32
+ }
33
+
34
+ impl Serialize for RustdocPage {
35
+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
36
+ where
37
+ S : Serializer ,
38
+ {
39
+ let mut state = serializer. serialize_struct ( "RustdocPage" , 9 ) ?;
40
+ state. serialize_field ( "rustdoc_head" , & self . head ) ?;
41
+ state. serialize_field ( "rustdoc_body" , & self . body ) ?;
42
+ state. serialize_field ( "rustdoc_body_class" , & self . body_class ) ?;
43
+ state. serialize_field ( "rustdoc_full" , & self . full ) ?;
44
+ state. serialize_field ( "rustdoc_status" , & true ) ?;
45
+ state. serialize_field ( "name" , & self . name ) ?;
46
+ state. serialize_field ( "version" , & self . version ) ?;
47
+ state. serialize_field ( "description" , & self . description ) ?;
48
+ state. serialize_field ( "crate_details" , & self . crate_details ) ?;
49
+
50
+ state. end ( )
51
+ }
52
+ }
20
53
21
54
#[ derive( Clone ) ]
22
55
pub struct RustLangRedirector {
@@ -30,7 +63,6 @@ impl RustLangRedirector {
30
63
. join ( target)
31
64
. expect ( "failed to append crate name to rust-lang.org base URL" ) ;
32
65
let url = Url :: from_generic_url ( url) . expect ( "failed to convert url::Url to iron::Url" ) ;
33
-
34
66
Self { url }
35
67
}
36
68
}
@@ -183,22 +215,6 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
183
215
}
184
216
}
185
217
186
- #[ derive( Debug , Clone , PartialEq , Serialize ) ]
187
- struct RustdocPage {
188
- latest_path : String ,
189
- latest_version : String ,
190
- inner_path : String ,
191
- is_latest_version : bool ,
192
- rustdoc_head : String ,
193
- rustdoc_body : String ,
194
- rustdoc_body_class : String ,
195
- krate : CrateDetails ,
196
- }
197
-
198
- impl_webpage ! {
199
- RustdocPage = "rustdoc/page.html" ,
200
- }
201
-
202
218
/// Serves documentation generated by rustdoc.
203
219
///
204
220
/// This includes all HTML files for an individual crate, as well as the `search-index.js`, which is
@@ -273,11 +289,11 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
273
289
274
290
// Get the crate's details from the database
275
291
// NOTE: we know this crate must exist because we just checked it above (or else `match_version` is buggy)
276
- let krate = cexpect ! ( req, CrateDetails :: new( & conn, & name, & version) ) ;
292
+ let crate_details = cexpect ! ( req, CrateDetails :: new( & conn, & name, & version) ) ;
277
293
278
294
// if visiting the full path to the default target, remove the target from the path
279
295
// expects a req_path that looks like `[/:target]/.*`
280
- if req_path. get ( 0 ) . copied ( ) == Some ( & krate . metadata . default_target ) {
296
+ if req_path. get ( 0 ) . copied ( ) == Some ( & crate_details . metadata . default_target ) {
281
297
return redirect ( & name, & version, & req_path[ 1 ..] ) ;
282
298
}
283
299
@@ -318,20 +334,19 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
318
334
319
335
let file_content = ctry ! ( req, String :: from_utf8( file. 0 . content) ) ;
320
336
// Extract the head and body of the rustdoc file so that we can insert it into our own html
321
- let ( rustdoc_head, rustdoc_body, mut rustdoc_body_class) =
322
- ctry ! ( req, utils:: extract_head_and_body( & file_content) ) ;
337
+ let ( head, body, mut body_class) = ctry ! ( req, utils:: extract_head_and_body( & file_content) ) ;
323
338
324
339
// Add the `rustdoc` classes to the html body
325
- if rustdoc_body_class . is_empty ( ) {
326
- rustdoc_body_class = "rustdoc container-rustdoc" . to_string ( ) ;
340
+ if body_class . is_empty ( ) {
341
+ body_class = "rustdoc container-rustdoc" . to_string ( ) ;
327
342
} else {
328
343
// rustdoc adds its own "rustdoc" class to the body
329
- rustdoc_body_class . push_str ( " container-rustdoc" ) ;
344
+ body_class . push_str ( " container-rustdoc" ) ;
330
345
}
331
346
332
347
rendering_time. step ( "find latest path" ) ;
333
348
334
- let latest_release = krate . latest_release ( ) ;
349
+ let latest_release = crate_details . latest_release ( ) ;
335
350
336
351
// Get the latest version of the crate
337
352
let latest_version = latest_release. version . to_owned ( ) ;
@@ -351,7 +366,7 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
351
366
"/{}/{}/{}" ,
352
367
name,
353
368
latest_version,
354
- path_for_version( & latest_path, & krate . doc_targets, & conn, & config)
369
+ path_for_version( & latest_path, & crate_details . doc_targets, & conn, & config)
355
370
)
356
371
} else {
357
372
format ! ( "/crate/{}/{}" , name, latest_version)
@@ -366,25 +381,35 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
366
381
// Drop the `rustdoc/:crate/:version[/:platform]` prefix
367
382
inner_path. drain ( ..3 ) . for_each ( drop) ;
368
383
369
- if inner_path. len ( ) > 1 && krate . doc_targets . iter ( ) . any ( |s| s == inner_path[ 0 ] ) {
384
+ if inner_path. len ( ) > 1 && crate_details . doc_targets . iter ( ) . any ( |s| s == inner_path[ 0 ] ) {
370
385
inner_path. remove ( 0 ) ;
371
386
}
372
387
373
388
inner_path. join ( "/" )
374
389
} ;
375
390
376
391
// Build the page of documentation
377
- RustdocPage {
378
- latest_path,
379
- latest_version,
380
- inner_path,
381
- is_latest_version,
382
- rustdoc_head,
383
- rustdoc_body,
384
- rustdoc_body_class,
385
- krate,
386
- }
387
- . into_response ( req)
392
+ let content = RustdocPage {
393
+ head,
394
+ body,
395
+ body_class,
396
+ name,
397
+ full : file_content,
398
+ version,
399
+ crate_details : Some ( crate_details) ,
400
+ ..Default :: default ( )
401
+ } ;
402
+
403
+ // Build the page served to the user while setting options for templating
404
+ Page :: new ( content)
405
+ . set_true ( "show_package_navigation" )
406
+ . set_true ( "package_navigation_documentation_tab" )
407
+ . set_true ( "package_navigation_show_platforms_tab" )
408
+ . set_bool ( "is_latest_version" , is_latest_version)
409
+ . set ( "latest_path" , & latest_path)
410
+ . set ( "latest_version" , & latest_version)
411
+ . set ( "inner_path" , & inner_path)
412
+ . to_resp ( "rustdoc" )
388
413
}
389
414
390
415
/// Checks whether the given path exists.
@@ -581,9 +606,12 @@ impl Handler for SharedResourceHandler {
581
606
582
607
#[ cfg( test) ]
583
608
mod test {
609
+ use super :: * ;
584
610
use crate :: test:: * ;
611
+ use chrono:: Utc ;
585
612
use kuchiki:: traits:: TendrilSink ;
586
613
use reqwest:: StatusCode ;
614
+ use serde_json:: json;
587
615
use std:: { collections:: BTreeMap , iter:: FromIterator } ;
588
616
589
617
fn try_latest_version_redirect (
@@ -1427,4 +1455,101 @@ mod test {
1427
1455
Ok ( ( ) )
1428
1456
} )
1429
1457
}
1458
+
1459
+ #[ test]
1460
+ fn serialize_rustdoc_page ( ) {
1461
+ let time = Utc :: now ( ) ;
1462
+
1463
+ let details = json ! ( {
1464
+ "name" : "rcc" ,
1465
+ "version" : "100.0.0" ,
1466
+ "description" : null,
1467
+ "authors" : [ ] ,
1468
+ "owners" : [ ] ,
1469
+ "authors_json" : null,
1470
+ "dependencies" : null,
1471
+ "release_time" : super :: super :: duration_to_str( time) ,
1472
+ "build_status" : true ,
1473
+ "last_successful_build" : null,
1474
+ "rustdoc_status" : true ,
1475
+ "repository_url" : null,
1476
+ "homepage_url" : null,
1477
+ "keywords" : null,
1478
+ "have_examples" : true ,
1479
+ "target_name" : "x86_64-unknown-linux-gnu" ,
1480
+ "releases" : [ ] ,
1481
+ "github" : true ,
1482
+ "yanked" : false ,
1483
+ "github_stars" : null,
1484
+ "github_forks" : null,
1485
+ "github_issues" : null,
1486
+ "metadata" : {
1487
+ "name" : "serde" ,
1488
+ "version" : "1.0.0" ,
1489
+ "description" : "serde does stuff" ,
1490
+ "target_name" : null,
1491
+ "rustdoc_status" : true ,
1492
+ "default_target" : "x86_64-unknown-linux-gnu"
1493
+ } ,
1494
+ "is_library" : true ,
1495
+ "doc_targets" : [ ] ,
1496
+ "license" : null,
1497
+ "documentation_url" : null
1498
+ } ) ;
1499
+
1500
+ let mut page = RustdocPage {
1501
+ head : "<head><title>Whee</title></head>" . to_string ( ) ,
1502
+ body : "<body><h1>idk</h1></body>" . to_string ( ) ,
1503
+ body_class : "docsrs-body" . to_string ( ) ,
1504
+ name : "rcc" . to_string ( ) ,
1505
+ full : "??" . to_string ( ) ,
1506
+ version : "100.0.100" . to_string ( ) ,
1507
+ description : Some ( "a Rust compiler in C. Wait, maybe the other way around" . to_string ( ) ) ,
1508
+ crate_details : Some ( CrateDetails :: default_tester ( time) ) ,
1509
+ } ;
1510
+
1511
+ let correct_json = json ! ( {
1512
+ "rustdoc_head" : "<head><title>Whee</title></head>" ,
1513
+ "rustdoc_body" : "<body><h1>idk</h1></body>" ,
1514
+ "rustdoc_body_class" : "docsrs-body" ,
1515
+ "rustdoc_full" : "??" ,
1516
+ "rustdoc_status" : true ,
1517
+ "name" : "rcc" ,
1518
+ "version" : "100.0.100" ,
1519
+ "description" : "a Rust compiler in C. Wait, maybe the other way around" ,
1520
+ "crate_details" : details
1521
+ } ) ;
1522
+
1523
+ assert_eq ! ( correct_json, serde_json:: to_value( & page) . unwrap( ) ) ;
1524
+
1525
+ page. description = None ;
1526
+ let correct_json = json ! ( {
1527
+ "rustdoc_head" : "<head><title>Whee</title></head>" ,
1528
+ "rustdoc_body" : "<body><h1>idk</h1></body>" ,
1529
+ "rustdoc_body_class" : "docsrs-body" ,
1530
+ "rustdoc_full" : "??" ,
1531
+ "rustdoc_status" : true ,
1532
+ "name" : "rcc" ,
1533
+ "version" : "100.0.100" ,
1534
+ "description" : null,
1535
+ "crate_details" : details
1536
+ } ) ;
1537
+
1538
+ assert_eq ! ( correct_json, serde_json:: to_value( & page) . unwrap( ) ) ;
1539
+
1540
+ page. crate_details = None ;
1541
+ let correct_json = json ! ( {
1542
+ "rustdoc_head" : "<head><title>Whee</title></head>" ,
1543
+ "rustdoc_body" : "<body><h1>idk</h1></body>" ,
1544
+ "rustdoc_body_class" : "docsrs-body" ,
1545
+ "rustdoc_full" : "??" ,
1546
+ "rustdoc_status" : true ,
1547
+ "name" : "rcc" ,
1548
+ "version" : "100.0.100" ,
1549
+ "description" : null,
1550
+ "crate_details" : null
1551
+ } ) ;
1552
+
1553
+ assert_eq ! ( correct_json, serde_json:: to_value( & page) . unwrap( ) ) ;
1554
+ }
1430
1555
}
0 commit comments