|
1 |
| -use crate::{ |
2 |
| - db::Pool, |
3 |
| - docbuilder::Limits, |
4 |
| - impl_webpage, |
5 |
| - web::{page::WebPage, MetaData}, |
6 |
| -}; |
| 1 | +use super::duration_to_str; |
| 2 | +use super::page::Page; |
| 3 | +use super::MetaData; |
| 4 | +use crate::db::Pool; |
| 5 | +use crate::docbuilder::Limits; |
7 | 6 | use chrono::{DateTime, NaiveDateTime, Utc};
|
8 |
| -use iron::{ |
9 |
| - headers::{ |
10 |
| - AccessControlAllowOrigin, CacheControl, CacheDirective, ContentType, Expires, HttpDate, |
11 |
| - }, |
12 |
| - status, IronResult, Request, Response, |
13 |
| -}; |
| 7 | +use iron::prelude::*; |
14 | 8 | use router::Router;
|
15 |
| -use serde::Serialize; |
| 9 | +use serde::ser::{Serialize, SerializeStruct, Serializer}; |
16 | 10 |
|
17 |
| -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] |
| 11 | +#[derive(Debug, Clone, PartialEq, Eq)] |
18 | 12 | pub(crate) struct Build {
|
19 | 13 | id: i32,
|
20 | 14 | rustc_version: String,
|
21 |
| - docsrs_version: String, |
| 15 | + cratesfyi_version: String, |
22 | 16 | build_status: bool,
|
23 | 17 | build_time: DateTime<Utc>,
|
24 | 18 | output: Option<String>,
|
25 | 19 | }
|
26 | 20 |
|
27 |
| -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] |
| 21 | +impl Serialize for Build { |
| 22 | + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 23 | + where |
| 24 | + S: Serializer, |
| 25 | + { |
| 26 | + let mut state = serializer.serialize_struct("Build", 7)?; |
| 27 | + state.serialize_field("id", &self.id)?; |
| 28 | + state.serialize_field("rustc_version", &self.rustc_version)?; |
| 29 | + state.serialize_field("cratesfyi_version", &self.cratesfyi_version)?; |
| 30 | + state.serialize_field("build_status", &self.build_status)?; |
| 31 | + state.serialize_field("build_time", &self.build_time.format("%+").to_string())?; // RFC 3339 |
| 32 | + state.serialize_field("build_time_relative", &duration_to_str(self.build_time))?; |
| 33 | + state.serialize_field("output", &self.output)?; |
| 34 | + |
| 35 | + state.end() |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +#[derive(Debug, Clone, PartialEq, Eq)] |
28 | 40 | struct BuildsPage {
|
29 | 41 | metadata: Option<MetaData>,
|
30 | 42 | builds: Vec<Build>,
|
31 |
| - build_log: Option<Build>, |
| 43 | + build_details: Option<Build>, |
32 | 44 | limits: Limits,
|
33 | 45 | }
|
34 | 46 |
|
35 |
| -impl_webpage! { |
36 |
| - BuildsPage = "crate/builds.html", |
| 47 | +impl Serialize for BuildsPage { |
| 48 | + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 49 | + where |
| 50 | + S: Serializer, |
| 51 | + { |
| 52 | + let mut state = serializer.serialize_struct("Buildspage", 4)?; |
| 53 | + state.serialize_field("metadata", &self.metadata)?; |
| 54 | + state.serialize_field("builds", &self.builds)?; |
| 55 | + state.serialize_field("build_details", &self.build_details)?; |
| 56 | + state.serialize_field("limits", &self.limits.for_website())?; |
| 57 | + |
| 58 | + state.end() |
| 59 | + } |
37 | 60 | }
|
38 | 61 |
|
39 | 62 | pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
|
@@ -65,54 +88,220 @@ pub fn build_list_handler(req: &mut Request) -> IronResult<Response> {
|
65 | 88 | &[&name, &version]
|
66 | 89 | ));
|
67 | 90 |
|
68 |
| - let mut build_log = None; |
| 91 | + let mut build_details = None; |
69 | 92 | // FIXME: getting builds.output may cause performance issues when release have tons of builds
|
70 |
| - let mut builds = query |
| 93 | + let mut build_list = query |
71 | 94 | .into_iter()
|
72 | 95 | .map(|row| {
|
73 |
| - let id: i32 = row.get("id"); |
| 96 | + let id: i32 = row.get(5); |
74 | 97 |
|
75 | 98 | let build = Build {
|
76 | 99 | id,
|
77 |
| - rustc_version: row.get("rustc_version"), |
78 |
| - docsrs_version: row.get("cratesfyi_version"), |
79 |
| - build_status: row.get("build_status"), |
80 |
| - build_time: DateTime::from_utc(row.get::<_, NaiveDateTime>("build_time"), Utc), |
81 |
| - output: row.get("output"), |
| 100 | + rustc_version: row.get(6), |
| 101 | + cratesfyi_version: row.get(7), |
| 102 | + build_status: row.get(8), |
| 103 | + build_time: DateTime::from_utc(row.get::<_, NaiveDateTime>(9), Utc), |
| 104 | + output: row.get(10), |
82 | 105 | };
|
83 | 106 |
|
84 | 107 | if id == req_build_id {
|
85 |
| - build_log = Some(build.clone()); |
| 108 | + build_details = Some(build.clone()); |
86 | 109 | }
|
87 | 110 |
|
88 | 111 | build
|
89 | 112 | })
|
90 | 113 | .collect::<Vec<Build>>();
|
91 | 114 |
|
92 | 115 | if req.url.path().join("/").ends_with(".json") {
|
| 116 | + use iron::headers::{ |
| 117 | + AccessControlAllowOrigin, CacheControl, CacheDirective, ContentType, Expires, HttpDate, |
| 118 | + }; |
| 119 | + use iron::status; |
| 120 | + |
93 | 121 | // Remove build output from build list for json output
|
94 |
| - for build in builds.iter_mut() { |
| 122 | + for build in build_list.as_mut_slice() { |
95 | 123 | build.output = None;
|
96 | 124 | }
|
97 | 125 |
|
98 |
| - let mut resp = Response::with((status::Ok, serde_json::to_string(&builds).unwrap())); |
99 |
| - resp.headers.set(ContentType::json()); |
| 126 | + let mut resp = Response::with((status::Ok, serde_json::to_string(&build_list).unwrap())); |
| 127 | + resp.headers |
| 128 | + .set(ContentType("application/json".parse().unwrap())); |
100 | 129 | resp.headers.set(Expires(HttpDate(time::now())));
|
101 | 130 | resp.headers.set(CacheControl(vec![
|
102 | 131 | CacheDirective::NoCache,
|
103 | 132 | CacheDirective::NoStore,
|
104 | 133 | CacheDirective::MustRevalidate,
|
105 | 134 | ]));
|
106 | 135 | resp.headers.set(AccessControlAllowOrigin::Any);
|
107 |
| - |
108 | 136 | Ok(resp)
|
109 | 137 | } else {
|
110 |
| - BuildsPage { |
| 138 | + let builds_page = BuildsPage { |
111 | 139 | metadata: MetaData::from_crate(&conn, &name, &version),
|
112 |
| - builds, |
113 |
| - build_log, |
| 140 | + builds: build_list, |
| 141 | + build_details, |
114 | 142 | limits,
|
115 |
| - } |
116 |
| - .into_response(req) |
| 143 | + }; |
| 144 | + Page::new(builds_page) |
| 145 | + .set_true("show_package_navigation") |
| 146 | + .set_true("package_navigation_builds_tab") |
| 147 | + .to_resp("builds") |
| 148 | + } |
| 149 | +} |
| 150 | + |
| 151 | +#[cfg(test)] |
| 152 | +mod tests { |
| 153 | + use super::*; |
| 154 | + use chrono::Utc; |
| 155 | + use serde_json::json; |
| 156 | + |
| 157 | + #[test] |
| 158 | + fn serialize_build() { |
| 159 | + let time = Utc::now(); |
| 160 | + let mut build = Build { |
| 161 | + id: 22, |
| 162 | + rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), |
| 163 | + cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), |
| 164 | + build_status: true, |
| 165 | + build_time: time, |
| 166 | + output: None, |
| 167 | + }; |
| 168 | + |
| 169 | + let correct_json = json!({ |
| 170 | + "id": 22, |
| 171 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 172 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 173 | + "build_time": time.format("%+").to_string(), |
| 174 | + "build_time_relative": duration_to_str(time), |
| 175 | + "output": null, |
| 176 | + "build_status": true |
| 177 | + }); |
| 178 | + |
| 179 | + assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); |
| 180 | + |
| 181 | + build.output = Some("some random stuff".to_string()); |
| 182 | + let correct_json = json!({ |
| 183 | + "id": 22, |
| 184 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 185 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 186 | + "build_time": time.format("%+").to_string(), |
| 187 | + "build_time_relative": duration_to_str(time), |
| 188 | + "output": "some random stuff", |
| 189 | + "build_status": true |
| 190 | + }); |
| 191 | + |
| 192 | + assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); |
| 193 | + } |
| 194 | + |
| 195 | + #[test] |
| 196 | + fn serialize_build_page() { |
| 197 | + let time = Utc::now(); |
| 198 | + let build = Build { |
| 199 | + id: 22, |
| 200 | + rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), |
| 201 | + cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), |
| 202 | + build_status: true, |
| 203 | + build_time: time, |
| 204 | + output: None, |
| 205 | + }; |
| 206 | + let limits = Limits::default(); |
| 207 | + let mut builds = BuildsPage { |
| 208 | + metadata: Some(MetaData { |
| 209 | + name: "serde".to_string(), |
| 210 | + version: "1.0.0".to_string(), |
| 211 | + description: Some("serde does stuff".to_string()), |
| 212 | + target_name: None, |
| 213 | + rustdoc_status: true, |
| 214 | + default_target: "x86_64-unknown-linux-gnu".to_string(), |
| 215 | + }), |
| 216 | + builds: vec![build.clone()], |
| 217 | + build_details: Some(build.clone()), |
| 218 | + limits: limits.clone(), |
| 219 | + }; |
| 220 | + |
| 221 | + let correct_json = json!({ |
| 222 | + "metadata": { |
| 223 | + "name": "serde", |
| 224 | + "version": "1.0.0", |
| 225 | + "description": "serde does stuff", |
| 226 | + "target_name": null, |
| 227 | + "rustdoc_status": true, |
| 228 | + "default_target": "x86_64-unknown-linux-gnu" |
| 229 | + }, |
| 230 | + "builds": [{ |
| 231 | + "id": 22, |
| 232 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 233 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 234 | + "build_time": time.format("%+").to_string(), |
| 235 | + "build_time_relative": duration_to_str(time), |
| 236 | + "output": null, |
| 237 | + "build_status": true |
| 238 | + }], |
| 239 | + "build_details": { |
| 240 | + "id": 22, |
| 241 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 242 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 243 | + "build_time": time.format("%+").to_string(), |
| 244 | + "build_time_relative": duration_to_str(time), |
| 245 | + "output": null, |
| 246 | + "build_status": true |
| 247 | + }, |
| 248 | + "limits": limits.for_website(), |
| 249 | + }); |
| 250 | + |
| 251 | + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); |
| 252 | + |
| 253 | + builds.metadata = None; |
| 254 | + let correct_json = json!({ |
| 255 | + "metadata": null, |
| 256 | + "builds": [{ |
| 257 | + "id": 22, |
| 258 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 259 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 260 | + "build_time": time.format("%+").to_string(), |
| 261 | + "build_time_relative": duration_to_str(time), |
| 262 | + "output": null, |
| 263 | + "build_status": true |
| 264 | + }], |
| 265 | + "build_details": { |
| 266 | + "id": 22, |
| 267 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 268 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 269 | + "build_time": time.format("%+").to_string(), |
| 270 | + "build_time_relative": duration_to_str(time), |
| 271 | + "output": null, |
| 272 | + "build_status": true |
| 273 | + }, |
| 274 | + "limits": limits.for_website(), |
| 275 | + }); |
| 276 | + |
| 277 | + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); |
| 278 | + |
| 279 | + builds.builds = Vec::new(); |
| 280 | + let correct_json = json!({ |
| 281 | + "metadata": null, |
| 282 | + "builds": [], |
| 283 | + "build_details": { |
| 284 | + "id": 22, |
| 285 | + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", |
| 286 | + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", |
| 287 | + "build_time": time.format("%+").to_string(), |
| 288 | + "build_time_relative": duration_to_str(time), |
| 289 | + "output": null, |
| 290 | + "build_status": true |
| 291 | + }, |
| 292 | + "limits": limits.for_website() |
| 293 | + }); |
| 294 | + |
| 295 | + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); |
| 296 | + |
| 297 | + builds.build_details = None; |
| 298 | + let correct_json = json!({ |
| 299 | + "metadata": null, |
| 300 | + "builds": [], |
| 301 | + "build_details": null, |
| 302 | + "limits": limits.for_website(), |
| 303 | + }); |
| 304 | + |
| 305 | + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); |
117 | 306 | }
|
118 | 307 | }
|
0 commit comments