diff --git a/Cargo.lock b/Cargo.lock index 19f7b5680..943335c34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,7 +363,7 @@ dependencies = [ "failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", - "handlebars-iron 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars-iron 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)", "html5ever 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "kuchiki 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -386,12 +386,13 @@ dependencies = [ "rusoto_core 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_credential 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusoto_s3 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustwide 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "schemamama 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "schemamama_postgres 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "slug 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "staticfile 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -846,7 +847,7 @@ dependencies = [ [[package]] name = "handlebars" -version = "0.24.2" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -854,20 +855,22 @@ dependencies = [ "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "handlebars-iron" -version = "0.22.0" +version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "handlebars 0.24.2 (registry+https://github.com/rust-lang/crates.io-index)", + "handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1859,7 +1862,7 @@ dependencies = [ "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", "postgres-protocol 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2464,6 +2467,15 @@ name = "safemem" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "same-file" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "same-file" version = "1.0.6" @@ -3434,10 +3446,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "walkdir" -version = "0.1.8" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -3686,8 +3699,8 @@ dependencies = [ "checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" "checksum git2 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "11e4b2082980e751c4bf4273e9cbb4a02c655729c8ee8a79f66cad03c8f4d31e" "checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" -"checksum handlebars 0.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1060eaf359ded4fc5827db4591949fff18b0b736821c94c347a441d7019c2545" -"checksum handlebars-iron 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8e885e22dfcf8fbb0ce797cb402b82b6cafea35100a42790bd84c32b718040" +"checksum handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04af2006ea09d985fef82b81e0eb25337e51b691c76403332378a53d521edc" +"checksum handlebars-iron 0.25.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b8ad7259b5bfcc65da1f1f3525eb5e4d5c4c6c7ce2d3b9c9945165e7e083c9c" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" "checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" @@ -3856,6 +3869,7 @@ dependencies = [ "checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" "checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" +"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" "checksum sass-rs 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cabcf7c6e55053f359911187ac401409aad2dc14338cae972dec266fee486abd" "checksum sass-sys 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "dd454d3c8fa19fe6c66df5d6ced4933f3a40b29d5875114eacc469451136226d" @@ -3966,7 +3980,7 @@ dependencies = [ "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c66c0b9792f0a765345452775f3adbd28dde9d33f30d13e5dcc5ae17cf6f3780" +"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" "checksum want 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" diff --git a/Cargo.toml b/Cargo.toml index 6023f66e2..4bb824643 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ edition = "2018" [dependencies] log = "0.4" -rustc-serialize = "0.3" regex = "1" structopt = "0.3" crates-index-diff = "7" @@ -41,10 +40,14 @@ rustwide = "=0.7.0" mime_guess = "2" dotenv = "0.15" +# Data serialization and deserialization +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + # iron dependencies iron = "0.5" router = "0.5" -handlebars-iron = "0.22" +handlebars-iron = "0.25" params = "0.8" staticfile = { version = "0.4", features = [ "cache" ] } tempfile = "3.1.0" @@ -60,7 +63,7 @@ path-slash = "0.1.1" [dependencies.postgres] version = "0.15" -features = [ "with-time", "with-rustc-serialize" ] +features = ["with-time", "with-serde_json"] [dev-dependencies] once_cell = "1.2.0" diff --git a/src/db/add_package.rs b/src/db/add_package.rs index 34e5ea15b..2c30f751b 100644 --- a/src/db/add_package.rs +++ b/src/db/add_package.rs @@ -10,12 +10,11 @@ use crate::{ index::api::{CrateOwner, RegistryCrateData}, utils::MetadataPackage, }; - use log::debug; use postgres::Connection; use regex::Regex; -use rustc_serialize::json::{Json, ToJson}; use semver::Version; +use serde_json::Value; use slug::slugify; /// Adds a package into database. @@ -31,7 +30,7 @@ pub(crate) fn add_package_into_database( source_dir: &Path, res: &BuildResult, default_target: &str, - source_files: Option, + source_files: Option, doc_targets: Vec, cratesio_data: &RegistryCrateData, has_docs: bool, @@ -88,7 +87,7 @@ pub(crate) fn add_package_into_database( &crate_id, &metadata_pkg.version, &cratesio_data.release_time, - &dependencies.to_json(), + &serde_json::to_value(&dependencies)?, &metadata_pkg.package_name(), &cratesio_data.yanked, &res.successful, @@ -100,12 +99,12 @@ pub(crate) fn add_package_into_database( &metadata_pkg.description, &rustdoc, &readme, - &metadata_pkg.authors.to_json(), - &metadata_pkg.keywords.to_json(), + &serde_json::to_value(&metadata_pkg.authors)?, + &serde_json::to_value(&metadata_pkg.keywords)?, &has_examples, &cratesio_data.downloads, &source_files, - &doc_targets.to_json(), + &serde_json::to_value(&doc_targets)?, &is_library, &res.rustc_version, &metadata_pkg.documentation, @@ -122,20 +121,21 @@ pub(crate) fn add_package_into_database( // Update versions { let metadata_version = Version::parse(&metadata_pkg.version)?; - let mut versions: Json = conn + let mut versions: Value = conn .query("SELECT versions FROM crates WHERE id = $1", &[&crate_id])? .get(0) .get(0); if let Some(versions_array) = versions.as_array_mut() { let mut found = false; for version in versions_array.clone() { - let version = Version::parse(version.as_string().unwrap())?; + let version = Version::parse(version.as_str().unwrap())?; if version == metadata_version { found = true; } } + if !found { - versions_array.push(metadata_pkg.version.to_string().to_json()); + versions_array.push(Value::String(metadata_pkg.version.clone())); } } let _ = conn.query( diff --git a/src/db/file.rs b/src/db/file.rs index d82e32900..3280d1e40 100644 --- a/src/db/file.rs +++ b/src/db/file.rs @@ -8,7 +8,7 @@ use crate::error::Result; use crate::storage::Storage; use postgres::Connection; -use rustc_serialize::json::{Json, ToJson}; +use serde_json::Value; use std::path::{Path, PathBuf}; pub(crate) use crate::storage::Blob; @@ -30,21 +30,24 @@ pub fn add_path_into_database>( conn: &Connection, prefix: &str, path: P, -) -> Result { +) -> Result { let mut backend = Storage::new(conn); let file_list = backend.store_all(conn, prefix, path.as_ref())?; file_list_to_json(file_list.into_iter().collect()) } -fn file_list_to_json(file_list: Vec<(PathBuf, String)>) -> Result { - let mut file_list_json: Vec = Vec::new(); +fn file_list_to_json(file_list: Vec<(PathBuf, String)>) -> Result { + let mut file_list_json: Vec = Vec::new(); for file in file_list { - let mut v: Vec = Vec::with_capacity(2); - v.push(file.1); - v.push(file.0.into_os_string().into_string().unwrap()); - file_list_json.push(v.to_json()); + let mut v = Vec::with_capacity(2); + v.push(Value::String(file.1)); + v.push(Value::String( + file.0.into_os_string().into_string().unwrap(), + )); + + file_list_json.push(Value::Array(v)); } - Ok(file_list_json.to_json()) + Ok(Value::Array(file_list_json)) } diff --git a/src/docbuilder/crates.rs b/src/docbuilder/crates.rs index 0c3a8db71..08de1d100 100644 --- a/src/docbuilder/crates.rs +++ b/src/docbuilder/crates.rs @@ -1,10 +1,9 @@ use crate::error::Result; use failure::err_msg; -use rustc_serialize::json::Json; -use std::fs; +use serde_json::Value; use std::io::prelude::*; use std::io::BufReader; -use std::path::PathBuf; +use std::{fs, path::PathBuf, str::FromStr}; fn crates_from_file(path: &PathBuf, func: &mut F) -> Result<()> where @@ -24,7 +23,7 @@ where continue; }; - let data = if let Ok(data) = Json::from_str(line.trim()) { + let data = if let Ok(data) = Value::from_str(line.trim()) { data } else { continue; @@ -35,19 +34,15 @@ where .ok_or_else(|| err_msg("Not a JSON object"))?; let crate_name = obj .get("name") - .and_then(|n| n.as_string()) + .and_then(|n| n.as_str()) .ok_or_else(|| err_msg("`name` not found in JSON object"))?; let vers = obj .get("vers") - .and_then(|n| n.as_string()) + .and_then(|n| n.as_str()) .ok_or_else(|| err_msg("`vers` not found in JSON object"))?; // Skip yanked crates - if obj - .get("yanked") - .and_then(|n| n.as_boolean()) - .unwrap_or(false) - { + if obj.get("yanked").and_then(|n| n.as_bool()).unwrap_or(false) { continue; } diff --git a/src/docbuilder/limits.rs b/src/docbuilder/limits.rs index 317f3c47f..3db3c45fc 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, Eq)] pub(crate) struct Limits { memory: usize, targets: usize, diff --git a/src/docbuilder/rustwide_builder.rs b/src/docbuilder/rustwide_builder.rs index 437e542df..5b9e05251 100644 --- a/src/docbuilder/rustwide_builder.rs +++ b/src/docbuilder/rustwide_builder.rs @@ -10,11 +10,11 @@ use crate::utils::{copy_doc_dir, parse_rustc_version, CargoMetadata}; use failure::ResultExt; use log::{debug, info, warn, LevelFilter}; use postgres::Connection; -use rustc_serialize::json::ToJson; use rustwide::cmd::{Command, SandboxBuilder}; use rustwide::logging::{self, LogStorage}; use rustwide::toolchain::ToolchainError; use rustwide::{Build, Crate, Toolchain, Workspace, WorkspaceBuilder}; +use serde_json::Value; use std::borrow::Cow; use std::collections::HashSet; use std::path::Path; @@ -246,7 +246,7 @@ impl RustwideBuilder { conn.query( "INSERT INTO config (name, value) VALUES ('rustc_version', $1) \ ON CONFLICT (name) DO UPDATE SET value = $1;", - &[&self.rustc_version.to_json()], + &[&Value::String(self.rustc_version.clone())], )?; Ok(()) diff --git a/src/index/api.rs b/src/index/api.rs index d85e8d7db..3dd480d34 100644 --- a/src/index/api.rs +++ b/src/index/api.rs @@ -4,7 +4,7 @@ use crate::{error::Result, utils::MetadataPackage}; use failure::err_msg; use reqwest::{header::ACCEPT, Client}; -use rustc_serialize::json::Json; +use serde_json::Value; use time::Timespec; pub(crate) struct RegistryCrateData { @@ -47,7 +47,7 @@ fn get_release_time_yanked_downloads(pkg: &MetadataPackage) -> Result<(time::Tim .send()?; let mut body = String::new(); res.read_to_string(&mut body).unwrap(); - let json = Json::from_str(&body[..]).unwrap(); + let json: Value = serde_json::from_str(&body[..])?; let versions = json .as_object() .and_then(|o| o.get("versions")) @@ -62,13 +62,13 @@ fn get_release_time_yanked_downloads(pkg: &MetadataPackage) -> Result<(time::Tim .ok_or_else(|| err_msg("Not a JSON object"))?; let version_num = version .get("num") - .and_then(|v| v.as_string()) + .and_then(|v| v.as_str()) .ok_or_else(|| err_msg("Not a JSON object"))?; if semver::Version::parse(version_num).unwrap().to_string() == pkg.version { let release_time_raw = version .get("created_at") - .and_then(|c| c.as_string()) + .and_then(|c| c.as_str()) .ok_or_else(|| err_msg("Not a JSON object"))?; release_time = Some( time::strptime(release_time_raw, "%Y-%m-%dT%H:%M:%S") @@ -79,7 +79,7 @@ fn get_release_time_yanked_downloads(pkg: &MetadataPackage) -> Result<(time::Tim yanked = Some( version .get("yanked") - .and_then(|c| c.as_boolean()) + .and_then(|c| c.as_bool()) .ok_or_else(|| err_msg("Not a JSON object"))?, ); @@ -114,7 +114,7 @@ fn get_owners(pkg: &MetadataPackage) -> Result> { // and so many unwraps... let mut body = String::new(); res.read_to_string(&mut body).unwrap(); - let json = Json::from_str(&body[..])?; + let json: Value = serde_json::from_str(&body[..])?; let mut result = Vec::new(); if let Some(owners) = json @@ -127,22 +127,22 @@ fn get_owners(pkg: &MetadataPackage) -> Result> { let avatar = owner .as_object() .and_then(|o| o.get("avatar")) - .and_then(|o| o.as_string()) + .and_then(|o| o.as_str()) .unwrap_or(""); let email = owner .as_object() .and_then(|o| o.get("email")) - .and_then(|o| o.as_string()) + .and_then(|o| o.as_str()) .unwrap_or(""); let login = owner .as_object() .and_then(|o| o.get("login")) - .and_then(|o| o.as_string()) + .and_then(|o| o.as_str()) .unwrap_or(""); let name = owner .as_object() .and_then(|o| o.get("name")) - .and_then(|o| o.as_string()) + .and_then(|o| o.as_str()) .unwrap_or(""); if login.is_empty() { diff --git a/src/utils/cargo_metadata.rs b/src/utils/cargo_metadata.rs index 769c01f3e..0894f1ddf 100644 --- a/src/utils/cargo_metadata.rs +++ b/src/utils/cargo_metadata.rs @@ -1,5 +1,6 @@ use crate::error::Result; use rustwide::{cmd::Command, Toolchain, Workspace}; +use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::path::Path; @@ -23,7 +24,7 @@ impl CargoMetadata { let mut iter = res.stdout_lines().iter(); let metadata = if let (Some(serialized), None) = (iter.next(), iter.next()) { - ::rustc_serialize::json::decode::(serialized)? + serde_json::from_str::(serialized)? } else { return Err(::failure::err_msg( "invalid output returned by `cargo metadata`", @@ -61,7 +62,7 @@ impl CargoMetadata { } } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] pub(crate) struct Package { pub(crate) id: String, pub(crate) name: String, @@ -104,7 +105,7 @@ impl Package { } } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] pub(crate) struct Target { pub(crate) name: String, #[cfg(not(test))] @@ -125,32 +126,32 @@ impl Target { } } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] pub(crate) struct Dependency { pub(crate) name: String, pub(crate) req: String, pub(crate) kind: Option, } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] struct DeserializedMetadata { packages: Vec, resolve: DeserializedResolve, } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] struct DeserializedResolve { root: String, nodes: Vec, } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] struct DeserializedResolveNode { id: String, deps: Vec, } -#[derive(RustcDecodable)] +#[derive(Deserialize, Serialize)] struct DeserializedResolveDep { pkg: String, } diff --git a/src/utils/github_updater.rs b/src/utils/github_updater.rs index 8cd96fd14..557343f42 100644 --- a/src/utils/github_updater.rs +++ b/src/utils/github_updater.rs @@ -2,6 +2,7 @@ use crate::{db::connect_db, error::Result}; use failure::err_msg; use log::debug; use regex::Regex; +use std::str::FromStr; /// Fields we need use in cratesfyi #[derive(Debug)] @@ -72,7 +73,7 @@ pub fn github_updater() -> Result<()> { } fn get_github_fields(path: &str) -> Result { - use rustc_serialize::json::Json; + use serde_json::Value; let body = { use reqwest::header::USER_AGENT; @@ -105,13 +106,13 @@ fn get_github_fields(path: &str) -> Result { body }; - let json = Json::from_str(&body[..])?; + let json = Value::from_str(&body[..])?; let obj = json.as_object().unwrap(); Ok(GitHubFields { description: obj .get("description") - .and_then(|d| d.as_string()) + .and_then(|d| d.as_str()) .unwrap_or("") .to_string(), stars: obj @@ -121,9 +122,7 @@ fn get_github_fields(path: &str) -> Result { forks: obj.get("forks_count").and_then(|d| d.as_i64()).unwrap_or(0), issues: obj.get("open_issues").and_then(|d| d.as_i64()).unwrap_or(0), last_commit: time::strptime( - obj.get("pushed_at") - .and_then(|d| d.as_string()) - .unwrap_or(""), + obj.get("pushed_at").and_then(|d| d.as_str()).unwrap_or(""), "%Y-%m-%dT%H:%M:%S", ) .unwrap_or_else(|_| time::now()) diff --git a/src/utils/release_activity_updater.rs b/src/utils/release_activity_updater.rs index 693d7f7be..c6d21caf0 100644 --- a/src/utils/release_activity_updater.rs +++ b/src/utils/release_activity_updater.rs @@ -1,7 +1,6 @@ use crate::db::connect_db; use crate::error::Result; -use rustc_serialize::json::ToJson; -use std::collections::BTreeMap; +use serde_json::{Map, Value}; use time::{now, Duration}; pub fn update_release_activity() -> Result<()> { @@ -52,12 +51,12 @@ pub fn update_release_activity() -> Result<()> { failure_counts.reverse(); let map = { - let mut map = BTreeMap::new(); - map.insert("dates".to_owned(), dates.to_json()); - map.insert("counts".to_owned(), crate_counts.to_json()); - map.insert("failures".to_owned(), failure_counts.to_json()); + let mut map = Map::new(); + map.insert("dates".to_owned(), serde_json::to_value(dates)?); + map.insert("counts".to_owned(), serde_json::to_value(crate_counts)?); + map.insert("failures".to_owned(), serde_json::to_value(failure_counts)?); - map.to_json() + Value::Object(map) }; conn.query( diff --git a/src/web/builds.rs b/src/web/builds.rs index ffc00cf58..b223a8c51 100644 --- a/src/web/builds.rs +++ b/src/web/builds.rs @@ -5,10 +5,9 @@ use super::MetaData; use crate::docbuilder::Limits; use iron::prelude::*; use router::Router; -use rustc_serialize::json::{Json, ToJson}; -use std::collections::BTreeMap; +use serde::ser::{Serialize, SerializeStruct, Serializer}; -#[derive(Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] struct Build { id: i32, rustc_version: String, @@ -18,6 +17,28 @@ struct Build { output: Option, } +impl Serialize for Build { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Build", 7)?; + state.serialize_field("id", &self.id)?; + state.serialize_field("rustc_version", &self.rustc_version)?; + state.serialize_field("cratesfyi_version", &self.cratesfyi_version)?; + state.serialize_field("build_status", &self.build_status)?; + state.serialize_field( + "build_time", + &time::at(self.build_time).rfc3339().to_string(), + )?; + state.serialize_field("build_time_relative", &duration_to_str(self.build_time))?; + state.serialize_field("output", &self.output)?; + + state.end() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] struct BuildsPage { metadata: Option, builds: Vec, @@ -25,37 +46,18 @@ struct BuildsPage { limits: Limits, } -impl ToJson for Build { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("id".to_owned(), self.id.to_json()); - m.insert("rustc_version".to_owned(), self.rustc_version.to_json()); - m.insert( - "cratesfyi_version".to_owned(), - self.cratesfyi_version.to_json(), - ); - m.insert("build_status".to_owned(), self.build_status.to_json()); - m.insert( - "build_time".to_owned(), - format!("{}", time::at(self.build_time).rfc3339()).to_json(), - ); - m.insert( - "build_time_relative".to_owned(), - duration_to_str(self.build_time).to_json(), - ); - m.insert("output".to_owned(), self.output.to_json()); - m.to_json() - } -} +impl Serialize for BuildsPage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Buildspage", 4)?; + state.serialize_field("metadata", &self.metadata)?; + state.serialize_field("builds", &self.builds)?; + state.serialize_field("build_details", &self.build_details)?; + state.serialize_field("limits", &self.limits.for_website())?; -impl ToJson for BuildsPage { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("metadata".to_owned(), self.metadata.to_json()); - m.insert("builds".to_owned(), self.builds.to_json()); - m.insert("build_details".to_owned(), self.build_details.to_json()); - m.insert("limits".into(), self.limits.for_website().to_json()); - m.to_json() + state.end() } } @@ -120,7 +122,7 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { build.output = None; } - let mut resp = Response::with((status::Ok, build_list.to_json().to_string())); + let mut resp = Response::with((status::Ok, serde_json::to_string(&build_list).unwrap())); resp.headers .set(ContentType("application/json".parse().unwrap())); resp.headers.set(Expires(HttpDate(time::now()))); @@ -144,3 +146,160 @@ pub fn build_list_handler(req: &mut Request) -> IronResult { .to_resp("builds") } } + +#[cfg(test)] +mod tests { + use super::*; + use serde_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 = json!({ + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "output": null, + "build_status": true + }); + + assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); + + build.output = Some("some random stuff".to_string()); + let correct_json = json!({ + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "output": "some random stuff", + "build_status": true + }); + + assert_eq!(correct_json, serde_json::to_value(&build).unwrap()); + } + + #[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 = json!({ + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "output": null, + "build_status": true + }, + "limits": limits.for_website(), + }); + + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); + + builds.metadata = None; + let correct_json = json!({ + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "output": null, + "build_status": true + }, + "limits": limits.for_website(), + }); + + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); + + builds.builds = Vec::new(); + let correct_json = json!({ + "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::at(time).rfc3339().to_string(), + "build_time_relative": duration_to_str(time), + "output": null, + "build_status": true + }, + "limits": limits.for_website() + }); + + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); + + builds.build_details = None; + let correct_json = json!({ + "metadata": null, + "builds": [], + "build_details": null, + "limits": limits.for_website(), + }); + + assert_eq!(correct_json, serde_json::to_value(&builds).unwrap()); + } +} diff --git a/src/web/crate_details.rs b/src/web/crate_details.rs index 022ea7891..c646a1cc0 100644 --- a/src/web/crate_details.rs +++ b/src/web/crate_details.rs @@ -8,8 +8,11 @@ use iron::prelude::*; use iron::{status, Url}; use postgres::Connection; use router::Router; -use rustc_serialize::json::{Json, ToJson}; -use std::collections::BTreeMap; +use serde::{ + ser::{SerializeStruct, Serializer}, + Serialize, +}; +use serde_json::Value; // TODO: Add target name and versions @@ -20,8 +23,8 @@ pub struct CrateDetails { description: Option, authors: Vec<(String, String)>, owners: Vec<(String, String)>, - authors_json: Option, - dependencies: Option, + authors_json: Option, + dependencies: Option, readme: Option, rustdoc: Option, // this is description_long in database release_time: time::Timespec, @@ -30,7 +33,7 @@ pub struct CrateDetails { rustdoc_status: bool, repository_url: Option, homepage_url: Option, - keywords: Option, + keywords: Option, have_examples: bool, // need to check this manually pub target_name: String, releases: Vec, @@ -46,72 +49,69 @@ pub struct CrateDetails { documentation_url: Option, } -impl ToJson for CrateDetails { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("name".to_string(), self.name.to_json()); - m.insert("version".to_string(), self.version.to_json()); - m.insert("description".to_string(), self.description.to_json()); - m.insert("authors".to_string(), self.authors.to_json()); - m.insert("owners".to_string(), self.owners.to_json()); - m.insert("authors_json".to_string(), self.authors_json.to_json()); - m.insert("dependencies".to_string(), self.dependencies.to_json()); +impl Serialize for CrateDetails { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Make sure that the length parameter passed to serde is correct by + // adding the someness of `readme` and `rustdoc` to the total. `true` + // is 1 and `false` is 0, so it increments if the value is some (and therefore + // needs to be serialized) + let mut state = serializer.serialize_struct( + "CrateDetails", + 26 + self.readme.is_some() as usize + self.rustdoc.is_some() as usize, + )?; + + state.serialize_field("metadata", &self.metadata)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("version", &self.version)?; + state.serialize_field("description", &self.description)?; + state.serialize_field("authors", &self.authors)?; + state.serialize_field("owners", &self.owners)?; + state.serialize_field("authors_json", &self.authors_json)?; + state.serialize_field("dependencies", &self.dependencies)?; + if let Some(ref readme) = self.readme { - m.insert("readme".to_string(), render_markdown(&readme).to_json()); + state.serialize_field("readme", &render_markdown(&readme))?; } + if let Some(ref rustdoc) = self.rustdoc { - m.insert("rustdoc".to_string(), render_markdown(&rustdoc).to_json()); + state.serialize_field("rustdoc", &render_markdown(&rustdoc))?; } - m.insert( - "release_time".to_string(), - duration_to_str(self.release_time).to_json(), - ); - m.insert("build_status".to_string(), self.build_status.to_json()); - m.insert( - "last_successful_build".to_string(), - self.last_successful_build.to_json(), - ); - m.insert("rustdoc_status".to_string(), self.rustdoc_status.to_json()); - m.insert("repository_url".to_string(), self.repository_url.to_json()); - m.insert("homepage_url".to_string(), self.homepage_url.to_json()); - m.insert("keywords".to_string(), self.keywords.to_json()); - m.insert("have_examples".to_string(), self.have_examples.to_json()); - m.insert("target_name".to_string(), self.target_name.to_json()); - m.insert("releases".to_string(), self.releases.to_json()); - m.insert("github".to_string(), self.github.to_json()); - m.insert("github_stars".to_string(), self.github_stars.to_json()); - m.insert("github_forks".to_string(), self.github_forks.to_json()); - m.insert("github_issues".to_string(), self.github_issues.to_json()); - m.insert("metadata".to_string(), self.metadata.to_json()); - m.insert("is_library".to_string(), self.is_library.to_json()); - m.insert("yanked".to_string(), self.yanked.to_json()); - m.insert("doc_targets".to_string(), self.doc_targets.to_json()); - m.insert("license".to_string(), self.license.to_json()); - m.insert( - "documentation_url".to_string(), - self.documentation_url.to_json(), - ); - m.to_json() + + state.serialize_field("release_time", &duration_to_str(self.release_time))?; + state.serialize_field("build_status", &self.build_status)?; + state.serialize_field("last_successful_build", &self.last_successful_build)?; + state.serialize_field("rustdoc_status", &self.rustdoc_status)?; + state.serialize_field("repository_url", &self.repository_url)?; + state.serialize_field("homepage_url", &self.homepage_url)?; + state.serialize_field("keywords", &self.keywords)?; + state.serialize_field("have_examples", &self.have_examples)?; + state.serialize_field("target_name", &self.target_name)?; + state.serialize_field("releases", &self.releases)?; + state.serialize_field("github", &self.github)?; + state.serialize_field("github_stars", &self.github_stars)?; + state.serialize_field("github_forks", &self.github_forks)?; + state.serialize_field("github_issues", &self.github_issues)?; + state.serialize_field("metadata", &self.metadata)?; + state.serialize_field("is_library", &self.is_library)?; + state.serialize_field("doc_targets", &self.doc_targets)?; + state.serialize_field("yanked", &self.yanked)?; + state.serialize_field("license", &self.license)?; + state.serialize_field("documentation_url", &self.documentation_url)?; + + state.end() } } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Serialize)] pub struct Release { pub version: String, pub build_status: bool, pub yanked: bool, } -impl ToJson for Release { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("version".to_string(), self.version.to_json()); - m.insert("build_status".to_string(), self.build_status.to_json()); - m.insert("yanked".to_string(), self.yanked.to_json()); - m.to_json() - } -} - impl CrateDetails { pub fn new(conn: &Connection, name: &str, version: &str) -> Option { // get all stuff, I love you rustfmt @@ -158,11 +158,11 @@ impl CrateDetails { // sort versions with semver let releases = { let mut versions: Vec = Vec::new(); - let versions_from_db: Json = rows.get(0).get(17); + let versions_from_db: Value = rows.get(0).get(17); if let Some(vers) = versions_from_db.as_array() { for version in vers { - if let Some(version) = version.as_string() { + if let Some(version) = version.as_str() { if let Ok(sem_ver) = semver::Version::parse(&version) { versions.push(sem_ver); } @@ -188,12 +188,12 @@ impl CrateDetails { }; let doc_targets = { - let data: Json = rows.get(0).get(23); + let data: Value = rows.get(0).get(23); data.as_array() .map(|array| { array .iter() - .filter_map(|item| item.as_string().map(|s| s.to_owned())) + .filter_map(|item| item.as_str().map(|s| s.to_owned())) .collect() }) .unwrap_or_else(Vec::new) @@ -284,6 +284,48 @@ impl CrateDetails { .find(|release| !release.yanked) .unwrap_or(&self.releases[0]) } + + #[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, + yanked: false, + 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 { @@ -343,6 +385,7 @@ mod tests { use super::*; use crate::test::TestDatabase; use failure::Error; + use serde_json::json; fn assert_last_successful_build_equals( db: &TestDatabase, @@ -587,4 +630,82 @@ mod tests { Ok(()) }) } + + #[test] + fn serialize_crate_details() { + let time = time::get_time(); + let mut details = CrateDetails::default_tester(time); + + let mut correct_json = json!({ + "name": "rcc", + "version": "100.0.0", + "description": null, + "authors": [], + "owners": [], + "authors_json": null, + "dependencies": null, + "release_time": super::super::duration_to_str(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, + "yanked": false, + "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 + }); + + assert_eq!(correct_json, serde_json::to_value(&details).unwrap()); + + let authors = vec![("Somebody".to_string(), "somebody@somebody.com".to_string())]; + let owners = vec![("Owner".to_string(), "owner@ownsstuff.com".to_string())]; + let description = "serde does stuff".to_string(); + + correct_json["description"] = Value::String(description.clone()); + correct_json["owners"] = serde_json::to_value(&owners).unwrap(); + correct_json["authors_json"] = serde_json::to_value(&authors).unwrap(); + correct_json["authors"] = serde_json::to_value(&authors).unwrap(); + + details.description = Some(description); + details.owners = owners; + details.authors_json = Some(serde_json::to_value(&authors).unwrap()); + details.authors = authors; + + assert_eq!(correct_json, serde_json::to_value(&details).unwrap()); + } + + #[test] + fn serialize_releases() { + let release = Release { + version: "idkman".to_string(), + build_status: true, + yanked: true, + }; + + let correct_json = json!({ + "version": "idkman", + "build_status": true, + "yanked": true, + }); + + assert_eq!(correct_json, serde_json::to_value(&release).unwrap()); + } } diff --git a/src/web/mod.rs b/src/web/mod.rs index c782e3dab..88dc7e980 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -63,14 +63,10 @@ use iron::prelude::*; use iron::{self, status, Handler, Listening, Url}; use postgres::Connection; use router::NoRoute; -use rustc_serialize::json::{Json, ToJson}; use semver::{Version, VersionReq}; use staticfile::Static; -use std::collections::BTreeMap; use std::net::SocketAddr; -use std::path::PathBuf; -use std::time::Duration; -use std::{env, fmt}; +use std::{env, fmt, path::PathBuf, time::Duration}; #[cfg(test)] use std::sync::{Arc, Mutex}; @@ -514,14 +510,14 @@ fn ico_handler(req: &mut Request) -> IronResult { } /// MetaData used in header -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)] 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 { @@ -554,25 +550,15 @@ impl MetaData { } } -impl ToJson for MetaData { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("name".to_owned(), self.name.to_json()); - m.insert("version".to_owned(), self.version.to_json()); - m.insert("description".to_owned(), self.description.to_json()); - m.insert("target_name".to_owned(), self.target_name.to_json()); - m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json()); - m.insert("default_target".to_owned(), self.default_target.to_json()); - - m.to_json() - } -} - #[cfg(test)] mod test { - use crate::test::*; - use crate::web::{handlebars_engine, match_version}; + use super::*; + use crate::{ + test::*, + web::{handlebars_engine, match_version}, + }; use html5ever::tendril::TendrilSink; + use serde_json::json; fn release(version: &str, db: &TestDatabase) -> i32 { db.fake_release() @@ -783,4 +769,51 @@ mod test { fn test_templates_are_valid() { handlebars_engine().expect("Failed to load handlebar templates"); } + + #[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!({ + "name": "serde", + "version": "1.0.0", + "description": "serde does stuff", + "target_name": null, + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }); + + assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap()); + + metadata.target_name = Some("x86_64-apple-darwin".to_string()); + let correct_json = json!({ + "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" + }); + + assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap()); + + metadata.description = None; + let correct_json = json!({ + "name": "serde", + "version": "1.0.0", + "description": null, + "target_name": "x86_64-apple-darwin", + "rustdoc_status": true, + "default_target": "x86_64-unknown-linux-gnu" + }); + + assert_eq!(correct_json, serde_json::to_value(&metadata).unwrap()); + } } diff --git a/src/web/page.rs b/src/web/page.rs index 46b8f0615..ef10f01c9 100644 --- a/src/web/page.rs +++ b/src/web/page.rs @@ -3,7 +3,8 @@ use handlebars_iron::Template; use iron::response::Response; use iron::{status, IronResult, Set}; -use rustc_serialize::json::{Json, ToJson}; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde_json::Value; use std::collections::BTreeMap; lazy_static::lazy_static! { @@ -22,8 +23,8 @@ fn load_rustc_resource_suffix() -> Result { failure::bail!("missing rustc version"); } - if let Some(Ok(vers)) = res.get(0).get_opt::<_, Json>("value") { - if let Some(vers_str) = vers.as_string() { + if let Some(Ok(vers)) = res.get(0).get_opt::<_, Value>("value") { + if let Some(vers_str) = vers.as_str() { return Ok(crate::utils::parse_rustc_version(vers_str)?); } } @@ -31,6 +32,7 @@ fn load_rustc_resource_suffix() -> Result { failure::bail!("failed to parse the rustc version"); } +#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize)] pub(crate) struct GlobalAlert { pub(crate) url: &'static str, pub(crate) text: &'static str, @@ -38,18 +40,8 @@ pub(crate) struct GlobalAlert { pub(crate) fa_icon: &'static str, } -impl ToJson for GlobalAlert { - fn to_json(&self) -> Json { - let mut map = BTreeMap::new(); - map.insert("url".to_string(), self.url.to_json()); - map.insert("text".to_string(), self.text.to_json()); - map.insert("css_class".to_string(), self.css_class.to_json()); - map.insert("fa_icon".to_string(), self.fa_icon.to_json()); - Json::Object(map) - } -} - -pub struct Page { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Page { title: Option, content: T, status: status::Status, @@ -59,7 +51,7 @@ pub struct Page { rustc_resource_suffix: &'static str, } -impl Page { +impl Page { pub fn new(content: T) -> Page { Page { title: None, @@ -119,42 +111,158 @@ impl Page { } } -impl ToJson for Page { - fn to_json(&self) -> Json { - let mut tree = BTreeMap::new(); +impl Serialize for Page { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + // Make sure that the length parameter passed to serde is correct by + // adding the someness of the global alert to the total. `true` + // is 1 and `false` is 0, so it increments if the value is some (and therefore + // needs to be serialized) + let mut state = + serializer.serialize_struct("Page", 9 + crate::GLOBAL_ALERT.is_some() as usize)?; if let Some(ref title) = self.title { - tree.insert("title".to_owned(), title.to_json()); + state.serialize_field("title", title)?; } - tree.insert( - "has_global_alert".to_owned(), - crate::GLOBAL_ALERT.is_some().to_json(), - ); + state.serialize_field("has_global_alert", &crate::GLOBAL_ALERT.is_some())?; if let Some(ref global_alert) = crate::GLOBAL_ALERT { - tree.insert("global_alert".to_owned(), global_alert.to_json()); + state.serialize_field("global_alert", global_alert)?; } - tree.insert("content".to_owned(), self.content.to_json()); - tree.insert( - "rustc_resource_suffix".to_owned(), - self.rustc_resource_suffix.to_json(), - ); - tree.insert( - "cratesfyi_version".to_owned(), - crate::BUILD_VERSION.to_json(), - ); - tree.insert( - "cratesfyi_version_safe".to_owned(), - crate::BUILD_VERSION - .replace(" ", "-") - .replace("(", "") - .replace(")", "") - .to_json(), + state.serialize_field("content", &self.content)?; + state.serialize_field("rustc_resource_suffix", self.rustc_resource_suffix)?; + state.serialize_field("cratesfyi_version", crate::BUILD_VERSION)?; + state.serialize_field( + "cratesfyi_version_safe", + &build_version_safe(crate::BUILD_VERSION), + )?; + state.serialize_field("varss", &self.varss)?; + state.serialize_field("varsb", &self.varsb)?; + state.serialize_field("varsi", &self.varsi)?; + + state.end() + } +} + +fn build_version_safe(version: &str) -> String { + version.replace(" ", "-").replace("(", "").replace(")", "") +} + +#[cfg(test)] +mod tests { + use super::super::releases::{self, Release}; + use super::*; + use iron::Url; + use serde_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 = json!({ + "content": [{ + "name": "lasso", + "version": "0.1.0", + "description": null, + "target_name": null, + "rustdoc_status": false, + "release_time": super::super::duration_to_str(time), + "release_time_rfc3339": time::at(time).rfc3339().to_string(), + "stars": 0 + }], + "varss": { "test": "works" }, + "varsb": { "test2": true }, + "varsi": { "test3": 1337 }, + "rustc_resource_suffix": &*RUSTC_RESOURCE_SUFFIX, + "cratesfyi_version": crate::BUILD_VERSION, + "cratesfyi_version_safe": build_version_safe(crate::BUILD_VERSION), + "has_global_alert": crate::GLOBAL_ALERT.is_some() + }); + + assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); + } + + #[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 WILL SOON END", + css_class: "THE END IS NEAR", + fa_icon: "https://gph.is/1uOvmqR", + }; + + let correct_json = json!({ + "url": "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", + "text": "THE WORLD WILL SOON END", + "css_class": "THE END IS NEAR", + "fa_icon": "https://gph.is/1uOvmqR" + }); + + assert_eq!(correct_json, serde_json::to_value(&alert).unwrap()); + } + + #[test] + fn build_version_url_safe() { + let safe = format!( + "https://docs.rs/builds/{}", + build_version_safe(crate::BUILD_VERSION) ); - tree.insert("varss".to_owned(), self.varss.to_json()); - tree.insert("varsb".to_owned(), self.varsb.to_json()); - tree.insert("varsi".to_owned(), self.varsi.to_json()); - Json::Object(tree) + assert!(Url::parse(&safe).is_ok()); } } diff --git a/src/web/releases.rs b/src/web/releases.rs index b9fa2c6c0..f45928491 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -8,8 +8,8 @@ use iron::prelude::*; use iron::status; use postgres::Connection; use router::Router; -use rustc_serialize::json::{Json, ToJson}; -use std::collections::BTreeMap; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde_json::Value; /// Number of release in home page const RELEASES_IN_HOME: i64 = 15; @@ -20,12 +20,12 @@ 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, } @@ -43,29 +43,30 @@ impl Default for Release { } } -impl ToJson for Release { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("name".to_string(), self.name.to_json()); - m.insert("version".to_string(), self.version.to_json()); - m.insert("description".to_string(), self.description.to_json()); - m.insert("target_name".to_string(), self.target_name.to_json()); - m.insert("rustdoc_status".to_string(), self.rustdoc_status.to_json()); - m.insert( - "release_time".to_string(), - duration_to_str(self.release_time).to_json(), - ); - m.insert( - "release_time_rfc3339".to_string(), - format!("{}", time::at(self.release_time).rfc3339()).to_json(), - ); - m.insert("stars".to_string(), self.stars.to_json()); - m.to_json() +impl Serialize for Release { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("Release", 8)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("version", &self.version)?; + state.serialize_field("description", &self.description)?; + state.serialize_field("target_name", &self.target_name)?; + state.serialize_field("rustdoc_status", &self.rustdoc_status)?; + state.serialize_field("release_time", &duration_to_str(self.release_time))?; + state.serialize_field( + "release_time_rfc3339", + &time::at(self.release_time).rfc3339().to_string(), + )?; + state.serialize_field("stars", &self.stars)?; + + state.end() } } #[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum Order { +pub(crate) enum Order { ReleaseTime, // this is default order GithubStars, RecentFailures, @@ -78,7 +79,7 @@ impl Default for Order { } } -fn get_releases(conn: &Connection, page: i64, limit: i64, order: Order) -> Vec { +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 @@ -619,7 +620,7 @@ pub fn search_handler(req: &mut Request) -> IronResult { pub fn activity_handler(req: &mut Request) -> IronResult { let conn = extension!(req, Pool).get()?; - let release_activity_data: Json = ctry!(conn.query( + let release_activity_data: Value = ctry!(conn.query( "SELECT value FROM config WHERE name = 'release_activity'", &[] )) @@ -670,6 +671,7 @@ pub fn build_queue_handler(req: &mut Request) -> IronResult { mod tests { use super::*; use crate::test::wrapper; + use serde_json::json; #[test] fn database_search() { @@ -1001,4 +1003,61 @@ mod tests { Ok(()) }) } + + #[test] + fn serialize_releases() { + let current_time = time::get_time(); + let now = time::at(current_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: current_time, + stars: 100, + }; + + let correct_json = json!({ + "name": "serde", + "version": "0.0.0", + "description": "serde makes things other things", + "target_name": "x86_64-pc-windows-msvc", + "rustdoc_status": true, + "release_time": duration_to_str(current_time), + "release_time_rfc3339": now.rfc3339().to_string(), + "stars": 100 + }); + + assert_eq!(correct_json, serde_json::to_value(&release).unwrap()); + + release.target_name = None; + let correct_json = json!({ + "name": "serde", + "version": "0.0.0", + "description": "serde makes things other things", + "target_name": null, + "rustdoc_status": true, + "release_time": duration_to_str(current_time), + "release_time_rfc3339": now.rfc3339().to_string(), + "stars": 100 + }); + + assert_eq!(correct_json, serde_json::to_value(&release).unwrap()); + + release.description = None; + let correct_json = json!({ + "name": "serde", + "version": "0.0.0", + "description": null, + "target_name": null, + "rustdoc_status": true, + "release_time": duration_to_str(current_time), + "release_time_rfc3339": now.rfc3339().to_string(), + "stars": 100 + }); + + assert_eq!(correct_json, serde_json::to_value(&release).unwrap()); + } } diff --git a/src/web/rustdoc.rs b/src/web/rustdoc.rs index 0e2a7c34a..8aada3783 100644 --- a/src/web/rustdoc.rs +++ b/src/web/rustdoc.rs @@ -15,8 +15,7 @@ use iron::Handler; use iron::{status, Url}; use postgres::Connection; use router::Router; -use rustc_serialize::json::{Json, ToJson}; -use std::collections::BTreeMap; +use serde::ser::{Serialize, SerializeStruct, Serializer}; #[derive(Debug, Default)] struct RustdocPage { @@ -30,19 +29,23 @@ struct RustdocPage { crate_details: Option, } -impl ToJson for RustdocPage { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - m.insert("rustdoc_head".to_string(), self.head.to_json()); - m.insert("rustdoc_body".to_string(), self.body.to_json()); - m.insert("rustdoc_body_class".to_string(), self.body_class.to_json()); - m.insert("rustdoc_full".to_string(), self.full.to_json()); - m.insert("rustdoc_status".to_string(), true.to_json()); - m.insert("name".to_string(), self.name.to_json()); - m.insert("version".to_string(), self.version.to_json()); - m.insert("description".to_string(), self.description.to_json()); - m.insert("crate_details".to_string(), self.crate_details.to_json()); - m.to_json() +impl Serialize for RustdocPage { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("RustdocPage", 9)?; + state.serialize_field("rustdoc_head", &self.head)?; + state.serialize_field("rustdoc_body", &self.body)?; + state.serialize_field("rustdoc_body_class", &self.body_class)?; + state.serialize_field("rustdoc_full", &self.full)?; + state.serialize_field("rustdoc_status", &true)?; + state.serialize_field("name", &self.name)?; + state.serialize_field("version", &self.version)?; + state.serialize_field("description", &self.description)?; + state.serialize_field("crate_details", &self.crate_details)?; + + state.end() } } @@ -541,8 +544,10 @@ impl Handler for SharedResourceHandler { #[cfg(test)] mod test { + use super::*; use crate::test::*; use reqwest::StatusCode; + use serde_json::json; use std::{collections::BTreeMap, iter::FromIterator}; fn try_latest_version_redirect( @@ -1343,4 +1348,100 @@ mod test { Ok(()) }) } + + #[test] + fn serialize_rustdoc_page() { + let time = time::get_time(); + let details = json!({ + "name": "rcc", + "version": "100.0.0", + "description": null, + "authors": [], + "owners": [], + "authors_json": null, + "dependencies": null, + "release_time": super::super::duration_to_str(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, + "yanked": false, + "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 + }); + + 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!({ + "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 + }); + + assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); + + page.description = None; + let correct_json = json!({ + "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 + }); + + assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); + + page.crate_details = None; + let correct_json = json!({ + "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 + }); + + assert_eq!(correct_json, serde_json::to_value(&page).unwrap()); + } } diff --git a/src/web/sitemap.rs b/src/web/sitemap.rs index 2eea4cd7d..fb1ffe7cc 100644 --- a/src/web/sitemap.rs +++ b/src/web/sitemap.rs @@ -2,7 +2,7 @@ use super::page::Page; use super::pool::Pool; use iron::headers::ContentType; use iron::prelude::*; -use rustc_serialize::json::{Json, ToJson}; +use serde_json::Value; use std::collections::BTreeMap; pub fn sitemap_handler(req: &mut Request) -> IronResult { @@ -41,16 +41,16 @@ pub fn about_handler(req: &mut Request) -> IronResult { let res = ctry!(conn.query("SELECT value FROM config WHERE name = 'rustc_version'", &[])); if let Some(row) = res.iter().next() { - if let Some(Ok::(res)) = row.get_opt(0) { - if let Some(vers) = res.as_string() { - content.insert("rustc_version".to_string(), vers.to_json()); + if let Some(Ok::(res)) = row.get_opt(0) { + if let Some(vers) = res.as_str() { + content.insert("rustc_version".to_string(), Value::String(vers.to_string())); } } } content.insert( "limits".to_string(), - crate::docbuilder::Limits::default().for_website().to_json(), + serde_json::to_value(&crate::docbuilder::Limits::default().for_website()).unwrap(), ); Page::new(content).title("About Docs.rs").to_resp("about") diff --git a/src/web/source.rs b/src/web/source.rs index b746555f9..a1fd60a65 100644 --- a/src/web/source.rs +++ b/src/web/source.rs @@ -7,10 +7,12 @@ use super::MetaData; use iron::prelude::*; use postgres::Connection; use router::Router; -use rustc_serialize::json::{Json, ToJson}; +use serde::ser::{Serialize, SerializeStruct, Serializer}; +use serde_json::Value; use std::cmp::Ordering; -use std::collections::BTreeMap; +use std::collections::HashMap; +/// A source file's type #[derive(PartialEq, PartialOrd)] enum FileType { Dir, @@ -19,28 +21,31 @@ enum FileType { RustSource, } +/// A source file #[derive(PartialEq, PartialOrd)] struct File { name: String, file_type: FileType, } +/// A list of source files struct FileList { metadata: MetaData, files: Vec, } -impl ToJson for FileList { - fn to_json(&self) -> Json { - let mut m: BTreeMap = BTreeMap::new(); - - m.insert("metadata".to_string(), self.metadata.to_json()); - - let mut file_vec: Vec = Vec::new(); +impl Serialize for FileList { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut state = serializer.serialize_struct("FileList", 2)?; + state.serialize_field("metadata", &self.metadata)?; + let mut files = Vec::with_capacity(self.files.len()); for file in &self.files { - let mut file_m: BTreeMap = BTreeMap::new(); - file_m.insert("name".to_string(), file.name.to_json()); + let mut map = HashMap::with_capacity(2); + map.insert("name", Value::String(file.name.to_owned())); let file_type = match file.file_type { FileType::Dir => "file_type_dir", @@ -48,13 +53,13 @@ impl ToJson for FileList { FileType::Binary => "file_type_binary", FileType::RustSource => "file_type_rust_source", }; + map.insert(file_type, Value::Bool(true)); - file_m.insert(file_type.to_string(), true.to_json()); - file_vec.push(file_m.to_json()); + files.push(map); } + state.serialize_field("files", &files)?; - m.insert("files".to_string(), file_vec.to_json()); - m.to_json() + state.end() } } @@ -65,10 +70,10 @@ impl FileList { /// /// ```text /// [ - /// ["text/plain",".gitignore"], - /// ["text/x-c","src/reseeding.rs"], - /// ["text/x-c","src/lib.rs"], - /// ["text/x-c","README.md"], + /// ["text/plain", ".gitignore"], + /// ["text/x-c", "src/reseeding.rs"], + /// ["text/x-c", "src/lib.rs"], + /// ["text/x-c", "README.md"], /// ... /// ] /// ``` @@ -97,15 +102,15 @@ impl FileList { return None; } - let files: Json = rows.get(0).get_opt(5).unwrap().ok()?; + let files: Value = rows.get(0).get_opt(5).unwrap().ok()?; let mut file_list: Vec = Vec::new(); if let Some(files) = files.as_array() { for file in files { if let Some(file) = file.as_array() { - let mime = file[0].as_string().unwrap(); - let path = file[1].as_string().unwrap(); + let mime = file[0].as_str().unwrap(); + let path = file[1].as_str().unwrap(); // skip .cargo-ok generated by cargo if path == ".cargo-ok" { @@ -249,3 +254,53 @@ pub fn source_browser_handler(req: &mut Request) -> IronResult { page.to_resp("source") } } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn serialize_file_list() { + let file_list = 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, + }, + ], + }; + + let correct_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 + }], + }); + + assert_eq!(correct_json, serde_json::to_value(&file_list).unwrap(),); + } +}