Skip to content

Commit 15b4027

Browse files
Nemo157Joshua Nelson
authored and
Joshua Nelson
committed
Split build details and build list pages
1 parent ab23ffd commit 15b4027

File tree

7 files changed

+187
-83
lines changed

7 files changed

+187
-83
lines changed

src/web/build_details.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::{
2+
db::Pool,
3+
impl_webpage,
4+
web::{page::WebPage, MetaData, Nope},
5+
};
6+
use chrono::{DateTime, Utc};
7+
use iron::{IronResult, Request, Response};
8+
use router::Router;
9+
use serde::Serialize;
10+
11+
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
12+
pub(crate) struct BuildDetails {
13+
id: i32,
14+
rustc_version: String,
15+
docsrs_version: String,
16+
build_status: bool,
17+
build_time: DateTime<Utc>,
18+
output: String,
19+
}
20+
21+
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
22+
struct BuildDetailsPage {
23+
metadata: MetaData,
24+
build_details: BuildDetails,
25+
}
26+
27+
impl_webpage! {
28+
BuildDetailsPage = "crate/build_details.html",
29+
}
30+
31+
pub fn build_details_handler(req: &mut Request) -> IronResult<Response> {
32+
let router = extension!(req, Router);
33+
let name = cexpect!(req, router.find("name"));
34+
let version = cexpect!(req, router.find("version"));
35+
let id: i32 = ctry!(req, cexpect!(req, router.find("id")).parse());
36+
37+
let mut conn = extension!(req, Pool).get()?;
38+
39+
let row = ctry!(
40+
req,
41+
conn.query_opt(
42+
"SELECT
43+
builds.rustc_version,
44+
builds.cratesfyi_version,
45+
builds.build_status,
46+
builds.build_time,
47+
builds.output
48+
FROM builds
49+
INNER JOIN releases ON releases.id = builds.rid
50+
INNER JOIN crates ON releases.crate_id = crates.id
51+
WHERE builds.id = $1 AND crates.name = $2 AND releases.version = $3",
52+
&[&id, &name, &version]
53+
)
54+
);
55+
56+
let build_details = if let Some(row) = row {
57+
BuildDetails {
58+
id,
59+
rustc_version: row.get("rustc_version"),
60+
docsrs_version: row.get("cratesfyi_version"),
61+
build_status: row.get("build_status"),
62+
build_time: row.get("build_time"),
63+
output: row.get("output"),
64+
}
65+
} else {
66+
return Err(Nope::BuildNotFound.into());
67+
};
68+
69+
BuildDetailsPage {
70+
metadata: cexpect!(req, MetaData::from_crate(&mut conn, &name, &version)),
71+
build_details,
72+
}
73+
.into_response(req)
74+
}
75+
76+
#[cfg(test)]
77+
mod tests {
78+
use crate::test::{wrapper, FakeBuild};
79+
use kuchiki::traits::TendrilSink;
80+
81+
#[test]
82+
fn build_logs() {
83+
wrapper(|env| {
84+
env.fake_release()
85+
.name("foo")
86+
.version("0.1.0")
87+
.builds(vec![FakeBuild::default().build_log("A build log")])
88+
.create()?;
89+
90+
let page = kuchiki::parse_html().one(
91+
env.frontend()
92+
.get("/crate/foo/0.1.0/builds")
93+
.send()?
94+
.text()?,
95+
);
96+
97+
let node = page.select("ul > li a.release").unwrap().next().unwrap();
98+
let attrs = node.attributes.borrow();
99+
let url = attrs.get("href").unwrap();
100+
101+
let page = kuchiki::parse_html().one(env.frontend().get(url).send()?.text()?);
102+
103+
let log = page.select("pre").unwrap().next().unwrap().text_contents();
104+
105+
assert!(log.contains("A build log"));
106+
107+
Ok(())
108+
});
109+
}
110+
111+
#[test]
112+
fn non_existing_build() {
113+
wrapper(|env| {
114+
env.fake_release().name("foo").version("0.1.0").create()?;
115+
116+
let res = env.frontend().get("/crate/foo/0.1.0/builds/42").send()?;
117+
assert_eq!(res.status(), 404);
118+
// TODO: blocked on https://github.com/rust-lang/docs.rs/issues/55
119+
// assert!(res.text()?.contains("no such build"));
120+
121+
Ok(())
122+
});
123+
}
124+
}

src/web/builds.rs

Lines changed: 9 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@ pub(crate) struct Build {
2121
docsrs_version: String,
2222
build_status: bool,
2323
build_time: DateTime<Utc>,
24-
output: Option<String>,
2524
}
2625

2726
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2827
struct BuildsPage {
2928
metadata: MetaData,
3029
builds: Vec<Build>,
31-
build_details: Option<Build>,
3230
limits: Limits,
3331
}
3432

@@ -40,7 +38,6 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
4038
let router = extension!(req, Router);
4139
let name = cexpect!(req, router.find("name"));
4240
let version = cexpect!(req, router.find("version"));
43-
let req_build_id: i32 = router.find("id").unwrap_or("0").parse().unwrap_or(0);
4441

4542
let mut conn = extension!(req, Pool).get()?;
4643
let limits = ctry!(req, Limits::for_crate(&mut conn, name));
@@ -57,8 +54,7 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
5754
builds.rustc_version,
5855
builds.cratesfyi_version,
5956
builds.build_status,
60-
builds.build_time,
61-
builds.output
57+
builds.build_time
6258
FROM builds
6359
INNER JOIN releases ON releases.id = builds.rid
6460
INNER JOIN crates ON releases.crate_id = crates.id
@@ -68,36 +64,18 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
6864
)
6965
);
7066

71-
let mut build_details = None;
72-
// FIXME: getting builds.output may cause performance issues when release have tons of builds
73-
let mut builds = query
67+
let builds: Vec<_> = query
7468
.into_iter()
75-
.map(|row| {
76-
let id: i32 = row.get("id");
77-
78-
let build = Build {
79-
id,
80-
rustc_version: row.get("rustc_version"),
81-
docsrs_version: row.get("cratesfyi_version"),
82-
build_status: row.get("build_status"),
83-
build_time: row.get("build_time"),
84-
output: row.get("output"),
85-
};
86-
87-
if id == req_build_id {
88-
build_details = Some(build.clone());
89-
}
90-
91-
build
69+
.map(|row| Build {
70+
id: row.get("id"),
71+
rustc_version: row.get("rustc_version"),
72+
docsrs_version: row.get("cratesfyi_version"),
73+
build_status: row.get("build_status"),
74+
build_time: row.get("build_time"),
9275
})
93-
.collect::<Vec<Build>>();
76+
.collect();
9477

9578
if req.url.path().join("/").ends_with(".json") {
96-
// Remove build output from build list for json output
97-
for build in builds.iter_mut() {
98-
build.output = None;
99-
}
100-
10179
let mut resp = Response::with((status::Ok, serde_json::to_string(&builds).unwrap()));
10280
resp.headers.set(ContentType::json());
10381
resp.headers.set(Expires(HttpDate(time::now())));
@@ -113,7 +91,6 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
11391
BuildsPage {
11492
metadata: cexpect!(req, MetaData::from_crate(&mut conn, &name, &version)),
11593
builds,
116-
build_details,
11794
limits,
11895
}
11996
.into_response(req)
@@ -299,34 +276,4 @@ mod tests {
299276
Ok(())
300277
});
301278
}
302-
303-
#[test]
304-
fn build_logs() {
305-
wrapper(|env| {
306-
env.fake_release()
307-
.name("foo")
308-
.version("0.1.0")
309-
.builds(vec![FakeBuild::default().build_log("A build log")])
310-
.create()?;
311-
312-
let page = kuchiki::parse_html().one(
313-
env.frontend()
314-
.get("/crate/foo/0.1.0/builds")
315-
.send()?
316-
.text()?,
317-
);
318-
319-
let node = page.select("ul > li a.release").unwrap().next().unwrap();
320-
let attrs = node.attributes.borrow();
321-
let url = attrs.get("href").unwrap();
322-
323-
let page = kuchiki::parse_html().one(env.frontend().get(url).send()?.text()?);
324-
325-
let log = page.select("pre").unwrap().next().unwrap().text_contents();
326-
327-
assert!(log.contains("A build log"));
328-
329-
Ok(())
330-
});
331-
}
332279
}

src/web/error.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::{error::Error, fmt};
99
#[derive(Debug, Copy, Clone)]
1010
pub enum Nope {
1111
ResourceNotFound,
12+
BuildNotFound,
1213
CrateNotFound,
1314
VersionNotFound,
1415
NoResults,
@@ -19,6 +20,7 @@ impl fmt::Display for Nope {
1920
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2021
f.write_str(match *self {
2122
Nope::ResourceNotFound => "Requested resource not found",
23+
Nope::BuildNotFound => "Requested build not found",
2224
Nope::CrateNotFound => "Requested crate not found",
2325
Nope::VersionNotFound => "Requested crate does not have specified version",
2426
Nope::NoResults => "Search yielded no results",
@@ -35,6 +37,7 @@ impl From<Nope> for IronError {
3537

3638
let status = match err {
3739
Nope::ResourceNotFound
40+
| Nope::BuildNotFound
3841
| Nope::CrateNotFound
3942
| Nope::VersionNotFound
4043
| Nope::NoResults => status::NotFound,
@@ -59,6 +62,13 @@ impl Handler for Nope {
5962
.into_response(req)
6063
}
6164

65+
Nope::BuildNotFound => ErrorPage {
66+
title: "The requested build does not exist",
67+
message: Some("no such build".into()),
68+
status: Status::NotFound,
69+
}
70+
.into_response(req),
71+
6272
Nope::CrateNotFound => {
6373
// user tried to navigate to a crate that doesn't exist
6474
// TODO: Display the attempted crate and a link to a search for said crate

src/web/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ macro_rules! extension {
7777
}};
7878
}
7979

80+
mod build_details;
8081
mod builds;
8182
mod crate_details;
8283
mod error;

src/web/routes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub(super) fn build_routes() -> Routes {
8989
);
9090
routes.internal_page(
9191
"/crate/:name/:version/builds/:id",
92-
super::builds::build_list_handler,
92+
super::build_details::build_details_handler,
9393
);
9494
routes.internal_page(
9595
"/crate/:name/:version/features",

templates/crate/build_details.html

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{%- extends "base.html" -%}
2+
{%- import "header/package_navigation.html" as navigation -%}
3+
4+
{%- block title -%}
5+
{{ macros::doc_title(name=metadata.name, version=metadata.version) }}
6+
{%- endblock title -%}
7+
8+
{%- block topbar -%}
9+
{%- set latest_version = "" -%}
10+
{%- set latest_path = "" -%}
11+
{%- set target = "" -%}
12+
{%- set inner_path = metadata.target_name ~ "/index.html" -%}
13+
{%- set is_latest_version = true -%}
14+
{%- set is_prerelease = false -%}
15+
{%- include "rustdoc/topbar.html" -%}
16+
{%- endblock topbar -%}
17+
18+
{%- block header -%}
19+
{{ navigation::package_navigation(metadata=metadata, active_tab="builds") }}
20+
{%- endblock header -%}
21+
22+
{%- block body -%}
23+
<div class="container">
24+
<div class="recent-releases-container">
25+
<div class="release">
26+
<strong>Build #{{ build_details.id }} {{ build_details.build_time | date(format="%+") }}</strong>
27+
</div>
28+
29+
{%- filter dedent -%}
30+
<pre>
31+
# rustc version
32+
{{ build_details.rustc_version }}
33+
# docs.rs version
34+
{{ build_details.docsrs_version }}
35+
36+
# build log
37+
{{ build_details.output }}
38+
</pre>
39+
{%- endfilter -%}
40+
</div>
41+
</div>
42+
{%- endblock body -%}

templates/crate/builds.html

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,6 @@
2222
{%- block body -%}
2323
<div class="container">
2424
<div class="recent-releases-container">
25-
{# If there is a build log then show it #}
26-
{# TODO: When viewing a build log, show a back button or a hide button #}
27-
{%- if build_details -%}
28-
<div class="release">
29-
<strong>Build #{{ build_details.id }} {{ build_details.build_time | date(format="%+") }}</strong>
30-
</div>
31-
32-
{%- filter dedent -%}
33-
<pre>
34-
# rustc version
35-
{{ build_details.rustc_version }}
36-
# docs.rs version
37-
{{ build_details.docsrs_version }}
38-
39-
# build log
40-
{{ build_details.output }}
41-
</pre>
42-
{%- endfilter -%}
43-
{%- endif -%}
44-
4525
<div class="release">
4626
<strong>Builds</strong>
4727
</div>

0 commit comments

Comments
 (0)