From 2b245d4a9eb7c169e7442510d7193cd05e0e73ee Mon Sep 17 00:00:00 2001 From: Chase Wilson Date: Sun, 3 May 2020 12:43:32 -0500 Subject: [PATCH] Added tests for json serialization --- src/docbuilder/limits.rs | 2 +- src/web/builds.rs | 213 +++++++++++++++++++++++++++++++++++++++ src/web/crate_details.rs | 157 +++++++++++++++++++++++++++++ src/web/mod.rs | 73 ++++++++++++-- src/web/page.rs | 127 +++++++++++++++++++++++ src/web/releases.rs | 97 ++++++++++++++++-- src/web/rustdoc.rs | 110 ++++++++++++++++++++ src/web/source.rs | 209 +++++++++++++++++++++++++++++++++++++- 8 files changed, 971 insertions(+), 17 deletions(-) diff --git a/src/docbuilder/limits.rs b/src/docbuilder/limits.rs index 317f3c47f..c0a368434 100644 --- a/src/docbuilder/limits.rs +++ b/src/docbuilder/limits.rs @@ -3,7 +3,7 @@ use postgres::Connection; use std::collections::BTreeMap; use std::time::Duration; -#[derive(Debug, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct Limits { memory: usize, targets: usize, diff --git a/src/web/builds.rs b/src/web/builds.rs index 3f42deddc..4542c2736 100644 --- a/src/web/builds.rs +++ b/src/web/builds.rs @@ -144,3 +144,216 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { .to_resp("builds") } } + +#[cfg(test)] +mod tests { + use super::*; + use rustc_serialize::json::Json; + + #[test] + fn serialize_build() { + let time = time::get_time(); + let mut build = Build { + id: 22, + rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), + cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), + build_status: true, + build_time: time, + output: None, + }; + + let correct_json = format!( + r#"{{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{}", + "build_time_relative": "{}", + "output": null, + "build_status": true + }}"#, + time::at(time).rfc3339().to_string(), + duration_to_str(time), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + build.to_json().to_string() + ); + + build.output = Some("some random stuff".to_string()); + let correct_json = format!( + r#"{{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{}", + "build_time_relative": "{}", + "output": "some random stuff", + "build_status": true + }}"#, + time::at(time).rfc3339().to_string(), + duration_to_str(time), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + build.to_json().to_string() + ); + } + + #[test] + fn serialize_build_page() { + let time = time::get_time(); + let build = Build { + id: 22, + rustc_version: "rustc 1.43.0 (4fb7144ed 2020-04-20)".to_string(), + cratesfyi_version: "docsrs 0.6.0 (3dd32ec 2020-05-01)".to_string(), + build_status: true, + build_time: time, + output: None, + }; + let limits = Limits::default(); + let mut builds = BuildsPage { + metadata: Some(MetaData { + name: "serde".to_string(), + version: "1.0.0".to_string(), + description: Some("serde does stuff".to_string()), + target_name: None, + rustdoc_status: true, + default_target: "x86_64-unknown-linux-gnu".to_string(), + }), + builds: vec![build.clone()], + build_details: Some(build.clone()), + limits: limits.clone(), + }; + + let correct_json = format!( + r#"{{ + "metadata": {{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }}, + "builds": [{{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{time}", + "build_time_relative": "{time_rel}", + "output": null, + "build_status": true + }}], + "build_details": {{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{time}", + "build_time_relative": "{time_rel}", + "output": null, + "build_status": true + }}, + "limits": {} + }}"#, + limits.for_website().to_json().to_string(), + time = time::at(time).rfc3339().to_string(), + time_rel = duration_to_str(time), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + builds.to_json().to_string() + ); + + builds.metadata = None; + let correct_json = format!( + r#"{{ + "metadata": null, + "builds": [{{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{time}", + "build_time_relative": "{time_rel}", + "output": null, + "build_status": true + }}], + "build_details": {{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{time}", + "build_time_relative": "{time_rel}", + "output": null, + "build_status": true + }}, + "limits": {} + }}"#, + limits.for_website().to_json().to_string(), + time = time::at(time).rfc3339().to_string(), + time_rel = duration_to_str(time), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + builds.to_json().to_string() + ); + + builds.builds = Vec::new(); + let correct_json = format!( + r#"{{ + "metadata": null, + "builds": [], + "build_details": {{ + "id": 22, + "rustc_version": "rustc 1.43.0 (4fb7144ed 2020-04-20)", + "cratesfyi_version": "docsrs 0.6.0 (3dd32ec 2020-05-01)", + "build_time": "{time}", + "build_time_relative": "{time_rel}", + "output": null, + "build_status": true + }}, + "limits": {} + }}"#, + limits.for_website().to_json().to_string(), + time = time::at(time).rfc3339().to_string(), + time_rel = duration_to_str(time), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + builds.to_json().to_string() + ); + + builds.build_details = None; + let correct_json = format!( + r#"{{ + "metadata": null, + "builds": [], + "build_details": null, + "limits": {} + }}"#, + limits.for_website().to_json().to_string(), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes the `id`s unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + builds.to_json().to_string() + ); + } +} diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index 602c493d3..321d792d1 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -276,6 +276,47 @@ impl CrateDetails { // releases will always contain at least one element &self.releases[0].version } + + #[cfg(test)] + pub fn default_tester(release_time: time::Timespec) -> Self { + Self { + name: "rcc".to_string(), + version: "100.0.0".to_string(), + description: None, + authors: vec![], + owners: vec![], + authors_json: None, + dependencies: None, + readme: None, + rustdoc: None, + release_time, + build_status: true, + last_successful_build: None, + rustdoc_status: true, + repository_url: None, + homepage_url: None, + keywords: None, + have_examples: true, + target_name: "x86_64-unknown-linux-gnu".to_string(), + releases: vec![], + github: true, + github_stars: None, + github_forks: None, + github_issues: None, + metadata: MetaData { + name: "serde".to_string(), + version: "1.0.0".to_string(), + description: Some("serde does stuff".to_string()), + target_name: None, + rustdoc_status: true, + default_target: "x86_64-unknown-linux-gnu".to_string(), + }, + is_library: true, + doc_targets: vec![], + license: None, + documentation_url: None, + } + } } fn map_to_release(conn: &Connection, crate_id: i32, version: String) -> Release { @@ -531,4 +572,120 @@ mod tests { Ok(()) }) } + + #[test] + fn serialize_crate_details() { + let time = time::get_time(); + let mut details = CrateDetails::default_tester(time); + + let correct_json = Json::from_str(&format!( + r#"{{ + "name": "rcc", + "version": "100.0.0", + "description": null, + "authors": [], + "owners": [], + "authors_json": null, + "dependencies": null, + "release_time": "{}", + "build_status": true, + "last_successful_build": null, + "rustdoc_status": true, + "repository_url": null, + "homepage_url": null, + "keywords": null, + "have_examples": true, + "target_name": "x86_64-unknown-linux-gnu", + "releases": [], + "github": true, + "github_stars": null, + "github_forks": null, + "github_issues": null, + "metadata": {{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }}, + "is_library": true, + "doc_targets": [], + "license": null, + "documentation_url": null + }}"#, + super::super::duration_to_str(time), + )) + .unwrap(); + + assert_eq!(correct_json, details.to_json()); + + details.description = Some("serde does stuff".to_string()); + details.owners = vec![("Owner".to_string(), "owner@ownsstuff.com".to_string())]; + + let authors = vec![("Somebody".to_string(), "somebody@somebody.com".to_string())]; + details.authors_json = Some(authors.to_json()); + details.authors = authors; + + let correct_json = Json::from_str(&format!( + r#"{{ + "name": "rcc", + "version": "100.0.0", + "description": "serde does stuff", + "authors": [["Somebody", "somebody@somebody.com"]], + "owners": [["Owner", "owner@ownsstuff.com"]], + "authors_json": [["Somebody", "somebody@somebody.com"]], + "dependencies": null, + "release_time": "{}", + "build_status": true, + "last_successful_build": null, + "rustdoc_status": true, + "repository_url": null, + "homepage_url": null, + "keywords": null, + "have_examples": true, + "target_name": "x86_64-unknown-linux-gnu", + "releases": [], + "github": true, + "github_stars": null, + "github_forks": null, + "github_issues": null, + "metadata": {{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }}, + "is_library": true, + "doc_targets": [], + "license": null, + "documentation_url": null + }}"#, + super::super::duration_to_str(time), + )) + .unwrap(); + + assert_eq!(correct_json, details.to_json()); + } + + #[test] + fn serialize_releases() { + let release = Release { + version: "idkman".to_string(), + build_status: true, + yanked: true, + }; + + let correct_json = Json::from_str( + r#"{ + "version": "idkman", + "build_status": true + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, release.to_json()); + } } diff --git a/src/web/mod.rs b/src/web/mod.rs index af4844cd9..8c2f13850 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -516,12 +516,12 @@ fn ico_handler(req: &mut Request) -> IronResult { /// MetaData used in header #[derive(Debug)] pub(crate) struct MetaData { - name: String, - version: String, - description: Option, - target_name: Option, - rustdoc_status: bool, - pub default_target: String, + pub(crate) name: String, + pub(crate) version: String, + pub(crate) description: Option, + pub(crate) target_name: Option, + pub(crate) rustdoc_status: bool, + pub(crate) default_target: String, } impl MetaData { @@ -570,9 +570,10 @@ impl ToJson for MetaData { #[cfg(test)] mod test { - use crate::test::*; - use crate::web::match_version; + use super::*; + use crate::{test::*, web::match_version}; use html5ever::tendril::TendrilSink; + use rustc_serialize::json::Json; fn release(version: &str, db: &TestDatabase) -> i32 { db.fake_release() @@ -778,4 +779,60 @@ mod test { Ok(()) }); } + + #[test] + fn serialize_metadata() { + let mut metadata = MetaData { + name: "serde".to_string(), + version: "1.0.0".to_string(), + description: Some("serde does stuff".to_string()), + target_name: None, + rustdoc_status: true, + default_target: "x86_64-unknown-linux-gnu".to_string(), + }; + + let correct_json = Json::from_str( + r#"{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, metadata.to_json()); + + metadata.target_name = Some("x86_64-apple-darwin".to_string()); + let correct_json = Json::from_str( + r#"{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": "x86_64-apple-darwin", + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, metadata.to_json()); + + metadata.description = None; + let correct_json = Json::from_str( + r#"{ + "name": "serde", + "version": "1.0.0", + "description": null, + "target_name": "x86_64-apple-darwin", + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, metadata.to_json()); + } } diff --git a/src/web/page.rs b/src/web/page.rs index 46b8f0615..85cfbceaa 100644 --- a/src/web/page.rs +++ b/src/web/page.rs @@ -31,6 +31,7 @@ fn load_rustc_resource_suffix() -> Result { failure::bail!("failed to parse the rustc version"); } +#[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct GlobalAlert { pub(crate) url: &'static str, pub(crate) text: &'static str, @@ -49,6 +50,7 @@ impl ToJson for GlobalAlert { } } +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Page { title: Option, content: T, @@ -158,3 +160,128 @@ impl ToJson for Page { Json::Object(tree) } } + +#[cfg(test)] +mod tests { + use super::super::releases::{self, Release}; + use super::*; + use rustc_serialize::json::Json; + + #[test] + fn serialize_page() { + let time = time::get_time(); + + let mut release = Release::default(); + release.name = "lasso".into(); + release.version = "0.1.0".into(); + release.release_time = time.clone(); + + let mut varss = BTreeMap::new(); + varss.insert("test".into(), "works".into()); + let mut varsb = BTreeMap::new(); + varsb.insert("test2".into(), true); + let mut varsi = BTreeMap::new(); + varsi.insert("test3".into(), 1337); + + let page = Page { + title: None, + content: vec![release.clone()], + status: status::Status::Ok, + varss, + varsb, + varsi, + rustc_resource_suffix: &*RUSTC_RESOURCE_SUFFIX, + }; + + let correct_json = format!( + r#"{{ + "content": [{{ + "name": "lasso", + "version": "0.1.0", + "description": null, + "target_name": null, + "rustdoc_status": false, + "release_time": "{}", + "release_time_rfc3339": "{}", + "stars": 0 + }}], + "varss": {{ "test": "works" }}, + "varsb": {{ "test2": true }}, + "varsi": {{ "test3": 1337 }}, + "rustc_resource_suffix": "{}", + "cratesfyi_version": "{}", + "cratesfyi_version_safe": "{}", + "has_global_alert": {} + }}"#, + super::super::duration_to_str(time.clone()), + time::at(time).rfc3339().to_string(), + &*RUSTC_RESOURCE_SUFFIX, + crate::BUILD_VERSION, + crate::BUILD_VERSION + .replace(" ", "-") + .replace("(", "") + .replace(")", ""), + crate::GLOBAL_ALERT.is_some(), + ); + + // Have to call `.to_string()` here because for some reason rustc_serialize defaults to + // u64s for `Json::from_str`, which makes everything in the respective `varsi` unequal + assert_eq!( + Json::from_str(&correct_json).unwrap().to_string(), + page.to_json().to_string() + ); + } + + #[test] + fn load_page_from_releases() { + crate::test::wrapper(|env| { + let db = env.db(); + db.fake_release().name("foo").version("0.1.0").create()?; + let packages = releases::get_releases(&db.conn(), 1, 1, releases::Order::ReleaseTime); + + let mut varsb = BTreeMap::new(); + varsb.insert("show_search_form".into(), true); + varsb.insert("hide_package_navigation".into(), true); + + let correct_page = Page { + title: None, + content: packages.clone(), + status: status::Status::Ok, + varss: BTreeMap::new(), + varsb, + varsi: BTreeMap::new(), + rustc_resource_suffix: &RUSTC_RESOURCE_SUFFIX, + }; + + let page = Page::new(packages) + .set_true("show_search_form") + .set_true("hide_package_navigation"); + + assert_eq!(page, correct_page); + + Ok(()) + }) + } + + #[test] + fn serialize_global_alert() { + let alert = GlobalAlert { + url: "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", + text: "THE WORLD IS ENDING", + css_class: "THE END IS NEAR", + fa_icon: "https://gph.is/1uOvmqR", + }; + + let correct_json = Json::from_str( + r#"{ + "url": "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", + "text": "THE WORLD IS ENDING", + "css_class": "THE END IS NEAR", + "fa_icon": "https://gph.is/1uOvmqR" + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, alert.to_json()); + } +} diff --git a/src/web/releases.rs b/src/web/releases.rs index a9a293cef..6826f6d87 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -18,13 +18,14 @@ const RELEASES_IN_RELEASES: i64 = 30; /// Releases in recent releases feed const RELEASES_IN_FEED: i64 = 150; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Release { - name: String, - version: String, + pub(crate) name: String, + pub(crate) version: String, description: Option, target_name: Option, rustdoc_status: bool, - release_time: time::Timespec, + pub(crate) release_time: time::Timespec, stars: i32, } @@ -56,21 +57,27 @@ impl ToJson for Release { ); m.insert( "release_time_rfc3339".to_string(), - format!("{}", time::at(self.release_time).rfc3339()).to_json(), + time::at(self.release_time).rfc3339().to_string().to_json(), ); m.insert("stars".to_string(), self.stars.to_json()); m.to_json() } } -enum Order { +pub(crate) enum Order { ReleaseTime, // this is default order GithubStars, RecentFailures, FailuresByGithubStars, } -fn get_releases(conn: &Connection, page: i64, limit: i64, order: Order) -> Vec { +impl Default for Order { + fn default() -> Self { + Self::ReleaseTime + } +} + +pub(crate) fn get_releases(conn: &Connection, page: i64, limit: i64, order: Order) -> Vec { let offset = (page - 1) * limit; // TODO: This function changed so much during development and current version have code @@ -633,3 +640,81 @@ pub fn build_queue_handler(req: &mut Request) -> IronResult { .set_true("releases_queue_tab") .to_resp("releases_queue") } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_releases() { + let time = time::get_time(); + let mut release = Release { + name: "serde".to_string(), + version: "0.0.0".to_string(), + description: Some("serde makes things other things".to_string()), + target_name: Some("x86_64-pc-windows-msvc".to_string()), + rustdoc_status: true, + release_time: time, + stars: 100, + }; + + let correct_json = Json::from_str(&format!( + r#"{{ + "name": "serde", + "version": "0.0.0", + "description": "serde makes things other things", + "target_name": "x86_64-pc-windows-msvc", + "rustdoc_status": true, + "release_time": "{}", + "release_time_rfc3339": "{}", + "stars": 100 + }}"#, + duration_to_str(time), + time::at(time).rfc3339().to_string(), + )) + .unwrap(); + + // Have to call `.to_string()` here because of how `rustc_serialize` handles integers + assert_eq!(correct_json.to_string(), release.to_json().to_string()); + + release.target_name = None; + let correct_json = Json::from_str(&format!( + r#"{{ + "name": "serde", + "version": "0.0.0", + "description": "serde makes things other things", + "target_name": null, + "rustdoc_status": true, + "release_time": "{}", + "release_time_rfc3339": "{}", + "stars": 100 + }}"#, + duration_to_str(time), + time::at(time).rfc3339().to_string(), + )) + .unwrap(); + + // Have to call `.to_string()` here because of how `rustc_serialize` handles integers + assert_eq!(correct_json.to_string(), release.to_json().to_string()); + + release.description = None; + let correct_json = Json::from_str(&format!( + r#"{{ + "name": "serde", + "version": "0.0.0", + "description": null, + "target_name": null, + "rustdoc_status": true, + "release_time": "{}", + "release_time_rfc3339": "{}", + "stars": 100 + }}"#, + duration_to_str(time), + time::at(time).rfc3339().to_string(), + )) + .unwrap(); + + // Have to call `.to_string()` here because of how `rustc_serialize` handles integers + assert_eq!(correct_json.to_string(), release.to_json().to_string()); + } +} diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index d7fb1d7ab..c5cfaf0ce 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -533,6 +533,7 @@ impl Handler for SharedResourceHandler { #[cfg(test)] mod test { + use super::*; use crate::test::*; use reqwest::StatusCode; use std::{collections::BTreeMap, iter::FromIterator}; @@ -1201,4 +1202,113 @@ mod test { Ok(()) }); } + + #[test] + fn serialize_rustdoc_page() { + let time = time::get_time(); + let details = format!( + r#"{{ + "name": "rcc", + "version": "100.0.0", + "description": null, + "authors": [], + "owners": [], + "authors_json": null, + "dependencies": null, + "release_time": "{}", + "build_status": true, + "last_successful_build": null, + "rustdoc_status": true, + "repository_url": null, + "homepage_url": null, + "keywords": null, + "have_examples": true, + "target_name": "x86_64-unknown-linux-gnu", + "releases": [], + "github": true, + "github_stars": null, + "github_forks": null, + "github_issues": null, + "metadata": {{ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }}, + "is_library": true, + "doc_targets": [], + "license": null, + "documentation_url": null + }}"#, + super::super::duration_to_str(time), + ); + + let mut page = RustdocPage { + head: "Whee".to_string(), + body: "

idk

".to_string(), + body_class: "docsrs-body".to_string(), + name: "rcc".to_string(), + full: "??".to_string(), + version: "100.0.100".to_string(), + description: Some("a Rust compiler in C. Wait, maybe the other way around".to_string()), + crate_details: Some(CrateDetails::default_tester(time)), + }; + + let correct_json = Json::from_str(&format!( + r#"{{ + "rustdoc_head": "Whee", + "rustdoc_body": "

idk

", + "rustdoc_body_class": "docsrs-body", + "rustdoc_full": "??", + "rustdoc_status": true, + "name": "rcc", + "version": "100.0.100", + "description": "a Rust compiler in C. Wait, maybe the other way around", + "crate_details": {} + }}"#, + details, + )) + .unwrap(); + + assert_eq!(correct_json, page.to_json()); + + page.description = None; + let correct_json = Json::from_str(&format!( + r#"{{ + "rustdoc_head": "Whee", + "rustdoc_body": "

idk

", + "rustdoc_body_class": "docsrs-body", + "rustdoc_full": "??", + "rustdoc_status": true, + "name": "rcc", + "version": "100.0.100", + "description": null, + "crate_details": {} + }}"#, + details, + )) + .unwrap(); + + assert_eq!(correct_json, page.to_json()); + + page.crate_details = None; + let correct_json = Json::from_str( + r#"{ + "rustdoc_head": "Whee", + "rustdoc_body": "

idk

", + "rustdoc_body_class": "docsrs-body", + "rustdoc_full": "??", + "rustdoc_status": true, + "name": "rcc", + "version": "100.0.100", + "description": null, + "crate_details": null + }"#, + ) + .unwrap(); + + assert_eq!(correct_json, page.to_json()); + } } diff --git a/src/web/source.rs b/src/web/source.rs index de10c7cb4..ad45decbd 100644 --- a/src/web/source.rs +++ b/src/web/source.rs @@ -9,8 +9,17 @@ use postgres::Connection; use router::Router; use rustc_serialize::json::{Json, ToJson}; use std::cmp::Ordering; -use std::collections::BTreeMap; - +use std::{collections::BTreeMap, iter::FromIterator}; + +/// A source file's type +/// +/// When serialized into json it takes this form +/// +/// * `FileType::Dir`: `{ "file_type_dir": true }` +/// * `FileType::Text`: `{ "file_type_text": true }` +/// * `FileType::Binary`: `{ "file_type_binary": true }` +/// * `FileType::RustSource`: `{ "file_type_rust_source": true }` +/// #[derive(PartialEq, PartialOrd)] enum FileType { Dir, @@ -19,12 +28,116 @@ enum FileType { RustSource, } +impl ToJson for FileType { + fn to_json(&self) -> Json { + let key = match self { + Self::Dir => "file_type_dir", + Self::Text => "file_type_text", + Self::Binary => "file_type_binary", + Self::RustSource => "file_type_rust_source", + }; + + Json::Object(BTreeMap::from_iter(vec![( + key.to_string(), + Json::Boolean(true), + )])) + } +} + +/// A source file +/// +/// Rust: +/// +/// ```ignore +/// struct File { +/// name: "main.rs", +/// file_type: FileType::RustSource, +/// } +/// ``` +/// +/// Json: +/// +/// ```json +/// { +/// "name": "main.rs", +/// "file_type_rust_source": true, +/// } +/// ``` +/// #[derive(PartialEq, PartialOrd)] struct File { name: String, file_type: FileType, } +impl ToJson for File { + fn to_json(&self) -> Json { + let mut file_m: BTreeMap = BTreeMap::new(); + file_m.insert("name".to_string(), self.name.to_json()); + + let file_type = match self.file_type { + FileType::Dir => "file_type_dir", + FileType::Text => "file_type_text", + FileType::Binary => "file_type_binary", + FileType::RustSource => "file_type_rust_source", + }; + + file_m.insert(file_type.to_string(), true.to_json()); + + Json::Object(file_m) + } +} + +/// A list of source files +/// +/// +/// Rust: +/// +/// ```ignore +/// FileList { +/// metadata: MetaData { +/// name: "rcc", +/// version: "0.0.0", +/// description: Some("it compiles an unholy language"), +/// target_name: None, +/// rustdoc_status: true, +/// default_target: "x86_64-unknown-linux-gnu", +/// }, +/// files: vec![ +/// File { +/// name: "main.rs", +/// file_type: FileType::RustSource, +/// }, +/// File { +/// name: "lib.rs", +/// file_type: FileType::RustSource, +/// }, +/// ], +/// } +/// ``` +/// +/// Json: +/// +/// ```json +/// { +/// "metadata": { +/// "name": "rcc", +/// "version": "0.0.0", +/// "description": "it compiles an unholy language", +/// "target_name": null, +/// "rustdoc_status": true, +/// "default_target": "x86_64-unknown-linux-gnu", +/// }, +/// "files": [{ +/// "name": "main.rs", +/// "file_type_rust_source": true, +/// }, { +/// "name": "lib.rs", +/// "file_type_rust_source": true, +/// }], +/// } +/// ``` +/// struct FileList { metadata: MetaData, files: Vec, @@ -36,6 +149,8 @@ impl ToJson for FileList { m.insert("metadata".to_string(), self.metadata.to_json()); + // TODO: file_vec from iter + let mut file_vec: Vec = Vec::new(); for file in &self.files { @@ -249,3 +364,93 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult { page.to_resp("source") } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_file_types() { + assert_eq!( + Json::from_str(r#"{ "file_type_dir": true }"#).unwrap(), + FileType::Dir.to_json(), + ); + assert_eq!( + Json::from_str(r#"{ "file_type_text": true }"#).unwrap(), + FileType::Text.to_json(), + ); + assert_eq!( + Json::from_str(r#"{ "file_type_binary": true }"#).unwrap(), + FileType::Binary.to_json(), + ); + assert_eq!( + Json::from_str(r#"{ "file_type_rust_source": true }"#).unwrap(), + FileType::RustSource.to_json(), + ); + } + + #[test] + fn serialize_file() { + assert_eq!( + Json::from_str( + r#"{ + "name": "main.rs", + "file_type_rust_source": true + }"# + ) + .unwrap(), + File { + name: "main.rs".to_string(), + file_type: FileType::RustSource + } + .to_json(), + ); + } + + #[test] + fn serialize_file_list() { + assert_eq!( + Json::from_str( + r#"{ + "metadata": { + "name": "rcc", + "version": "0.0.0", + "description": "it compiles an unholy language", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }, + "files": [{ + "name": "main.rs", + "file_type_rust_source": true + }, { + "name": "lib.rs", + "file_type_rust_source": true + }] + }"# + ) + .unwrap(), + FileList { + metadata: MetaData { + name: "rcc".to_string(), + version: "0.0.0".to_string(), + description: Some("it compiles an unholy language".to_string()), + target_name: None, + rustdoc_status: true, + default_target: "x86_64-unknown-linux-gnu".to_string(), + }, + files: vec![ + File { + name: "main.rs".to_string(), + file_type: FileType::RustSource + }, + File { + name: "lib.rs".to_string(), + file_type: FileType::RustSource + } + ], + } + .to_json(), + ); + } +}