diff --git a/src/badge.rs b/src/badge.rs index a8b4a902071..707b3c685fb 100644 --- a/src/badge.rs +++ b/src/badge.rs @@ -1,11 +1,11 @@ -use krate::Crate; -use schema::badges; - use diesel::pg::Pg; use diesel::prelude::*; use serde_json; use std::collections::HashMap; +use models::Crate; +use schema::badges; + #[derive(Debug, PartialEq, Clone, Deserialize, Serialize)] #[serde(rename_all = "kebab-case", tag = "badge_type", content = "attributes")] pub enum Badge { diff --git a/src/bin/delete-crate.rs b/src/bin/delete-crate.rs index bcad6039364..d7751481ff6 100644 --- a/src/bin/delete-crate.rs +++ b/src/bin/delete-crate.rs @@ -15,7 +15,7 @@ use std::env; use std::io; use std::io::prelude::*; -use cargo_registry::Crate; +use cargo_registry::models::Crate; use cargo_registry::schema::crates; #[allow(dead_code)] diff --git a/src/bin/delete-version.rs b/src/bin/delete-version.rs index c3fc2538806..b40cd44c728 100644 --- a/src/bin/delete-version.rs +++ b/src/bin/delete-version.rs @@ -15,7 +15,7 @@ use std::env; use std::io; use std::io::prelude::*; -use cargo_registry::{Crate, Version}; +use cargo_registry::models::{Crate, Version}; use cargo_registry::schema::versions; #[allow(dead_code)] diff --git a/src/bin/render-readmes.rs b/src/bin/render-readmes.rs index 647003dd2ae..d0857b22394 100644 --- a/src/bin/render-readmes.rs +++ b/src/bin/render-readmes.rs @@ -32,10 +32,12 @@ use std::thread; use tar::Archive; use url::Url; -use cargo_registry::{Config, Version}; -use cargo_registry::schema::*; +use cargo_registry::Config; use cargo_registry::render::readme_to_html; +use cargo_registry::models::Version; +use cargo_registry::schema::*; + const DEFAULT_PAGE_SIZE: usize = 25; const USAGE: &str = " Usage: render-readmes [options] diff --git a/src/bin/transfer-crates.rs b/src/bin/transfer-crates.rs index ad2c2ff7198..bf4543568f1 100644 --- a/src/bin/transfer-crates.rs +++ b/src/bin/transfer-crates.rs @@ -13,8 +13,7 @@ use std::env; use std::io; use std::io::prelude::*; -use cargo_registry::{Crate, User}; -use cargo_registry::owner::OwnerKind; +use cargo_registry::models::{Crate, OwnerKind, User}; use cargo_registry::schema::*; fn main() { diff --git a/src/bin/update-downloads.rs b/src/bin/update-downloads.rs index 9a4c2269e10..683d96fa177 100644 --- a/src/bin/update-downloads.rs +++ b/src/bin/update-downloads.rs @@ -8,7 +8,7 @@ use diesel::prelude::*; use std::env; use std::time::Duration; -use cargo_registry::VersionDownload; +use cargo_registry::models::VersionDownload; use cargo_registry::schema::*; static LIMIT: i64 = 1000; @@ -118,9 +118,7 @@ mod test { use diesel::insert_into; use super::*; use cargo_registry::env; - use cargo_registry::krate::{Crate, NewCrate}; - use cargo_registry::user::{NewUser, User}; - use cargo_registry::version::{NewVersion, Version}; + use cargo_registry::models::{Crate, NewCrate, NewUser, NewVersion, User, Version}; fn conn() -> PgConnection { let conn = PgConnection::establish(&env("TEST_DATABASE_URL")).unwrap(); diff --git a/src/controllers/category.rs b/src/controllers/category.rs new file mode 100644 index 00000000000..361c75b5c43 --- /dev/null +++ b/src/controllers/category.rs @@ -0,0 +1,89 @@ +use super::prelude::*; + +use models::Category; +use schema::categories; +use views::{EncodableCategory, EncodableCategoryWithSubcategories}; + +/// Handles the `GET /categories` route. +pub fn index(req: &mut Request) -> CargoResult { + let conn = req.db_conn()?; + let (offset, limit) = req.pagination(10, 100)?; + let query = req.query(); + let sort = query.get("sort").map_or("alpha", String::as_str); + + let categories = Category::toplevel(&conn, sort, limit, offset)?; + let categories = categories.into_iter().map(Category::encodable).collect(); + + // Query for the total count of categories + let total = Category::count_toplevel(&conn)?; + + #[derive(Serialize)] + struct R { + categories: Vec, + meta: Meta, + } + #[derive(Serialize)] + struct Meta { + total: i64, + } + + Ok(req.json(&R { + categories: categories, + meta: Meta { total: total }, + })) +} + +/// Handles the `GET /categories/:category_id` route. +pub fn show(req: &mut Request) -> CargoResult { + let slug = &req.params()["category_id"]; + let conn = req.db_conn()?; + let cat = categories::table + .filter(categories::slug.eq(::lower(slug))) + .first::(&*conn)?; + let subcats = cat.subcategories(&conn)? + .into_iter() + .map(Category::encodable) + .collect(); + + let cat = cat.encodable(); + let cat_with_subcats = EncodableCategoryWithSubcategories { + id: cat.id, + category: cat.category, + slug: cat.slug, + description: cat.description, + created_at: cat.created_at, + crates_cnt: cat.crates_cnt, + subcategories: subcats, + }; + + #[derive(Serialize)] + struct R { + category: EncodableCategoryWithSubcategories, + } + Ok(req.json(&R { + category: cat_with_subcats, + })) +} + +/// Handles the `GET /category_slugs` route. +pub fn slugs(req: &mut Request) -> CargoResult { + let conn = req.db_conn()?; + let slugs = categories::table + .select((categories::slug, categories::slug)) + .order(categories::slug) + .load(&*conn)?; + + #[derive(Serialize, Queryable)] + struct Slug { + id: String, + slug: String, + } + + #[derive(Serialize)] + struct R { + category_slugs: Vec, + } + Ok(req.json(&R { + category_slugs: slugs, + })) +} diff --git a/src/controllers/helpers/mod.rs b/src/controllers/helpers/mod.rs new file mode 100644 index 00000000000..ad71528010e --- /dev/null +++ b/src/controllers/helpers/mod.rs @@ -0,0 +1,3 @@ +pub mod pagination; + +pub use self::pagination::Paginate; diff --git a/src/pagination.rs b/src/controllers/helpers/pagination.rs similarity index 98% rename from src/pagination.rs rename to src/controllers/helpers/pagination.rs index dcaa9ff6305..9612c814143 100644 --- a/src/pagination.rs +++ b/src/controllers/helpers/pagination.rs @@ -3,6 +3,7 @@ use diesel::query_builder::*; use diesel::sql_types::BigInt; use diesel::pg::Pg; +#[derive(Debug)] pub struct Paginated { query: T, limit: i64, diff --git a/src/controllers/keyword.rs b/src/controllers/keyword.rs new file mode 100644 index 00000000000..d0356a222f4 --- /dev/null +++ b/src/controllers/keyword.rs @@ -0,0 +1,62 @@ +use super::prelude::*; + +use controllers::helpers::Paginate; +use models::Keyword; +use views::EncodableKeyword; + +/// Handles the `GET /keywords` route. +pub fn index(req: &mut Request) -> CargoResult { + use schema::keywords; + + let conn = req.db_conn()?; + let (offset, limit) = req.pagination(10, 100)?; + let query = req.query(); + let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha"); + + let mut query = keywords::table.into_boxed(); + + if sort == "crates" { + query = query.order(keywords::crates_cnt.desc()); + } else { + query = query.order(keywords::keyword.asc()); + } + + let data = query + .paginate(limit, offset) + .load::<(Keyword, i64)>(&*conn)?; + let total = data.get(0).map(|&(_, t)| t).unwrap_or(0); + let kws = data.into_iter() + .map(|(k, _)| k.encodable()) + .collect::>(); + + #[derive(Serialize)] + struct R { + keywords: Vec, + meta: Meta, + } + #[derive(Serialize)] + struct Meta { + total: i64, + } + + Ok(req.json(&R { + keywords: kws, + meta: Meta { total: total }, + })) +} + +/// Handles the `GET /keywords/:keyword_id` route. +pub fn show(req: &mut Request) -> CargoResult { + let name = &req.params()["keyword_id"]; + let conn = req.db_conn()?; + + let kw = Keyword::find_by_keyword(&conn, name)?; + + #[derive(Serialize)] + struct R { + keyword: EncodableKeyword, + } + Ok(req.json(&R { + keyword: kw.encodable(), + })) +} diff --git a/src/controllers/mod.rs b/src/controllers/mod.rs new file mode 100644 index 00000000000..7eb84eeef3a --- /dev/null +++ b/src/controllers/mod.rs @@ -0,0 +1,15 @@ +// TODO: Finish moving api endpoints to submodules here + +mod prelude { + pub use diesel::prelude::*; + + pub use conduit::{Request, Response}; + pub use conduit_router::RequestParams; + pub use db::RequestTransaction; + pub use util::{CargoResult, RequestUtils}; +} + +pub mod helpers; + +pub mod category; +pub mod keyword; diff --git a/src/crate_owner_invitation.rs b/src/crate_owner_invitation.rs index 0834a2411bd..59376405594 100644 --- a/src/crate_owner_invitation.rs +++ b/src/crate_owner_invitation.rs @@ -4,11 +4,12 @@ use diesel::prelude::*; use serde_json; use db::RequestTransaction; -use schema::{crate_owner_invitations, crate_owners, crates, users}; use user::RequestUser; use util::errors::{human, CargoResult}; use util::RequestUtils; -use owner::{CrateOwner, OwnerKind}; + +use models::{CrateOwner, OwnerKind}; +use schema::{crate_owner_invitations, crate_owners, crates, users}; /// The model representing a row in the `crate_owner_invitations` database table. #[derive(Clone, Copy, Debug, PartialEq, Eq, Identifiable, Queryable)] diff --git a/src/dependency.rs b/src/dependency.rs index 9caeeafcc59..a216a28aa29 100644 --- a/src/dependency.rs +++ b/src/dependency.rs @@ -5,10 +5,10 @@ use diesel::row::NamedRow; use semver; use git; -use krate::Crate; -use schema::*; use util::{human, CargoResult}; -use version::Version; + +use models::{Crate, Version}; +use schema::*; #[derive(Identifiable, Associations, Debug)] #[belongs_to(Version)] @@ -86,7 +86,7 @@ impl ReverseDependency { pub fn add_dependencies( conn: &PgConnection, - deps: &[::upload::CrateDependency], + deps: &[::views::EncodableCrateDependency], target_version_id: i32, ) -> CargoResult> { use diesel::insert_into; diff --git a/src/download.rs b/src/download.rs index b9a430f4891..922b4b2a1da 100644 --- a/src/download.rs +++ b/src/download.rs @@ -2,8 +2,8 @@ use chrono::NaiveDate; use diesel; use diesel::prelude::*; +use models::Version; use schema::version_downloads; -use version::Version; #[derive(Queryable, Identifiable, Associations, Debug, Clone, Copy)] #[belongs_to(Version)] diff --git a/src/git.rs b/src/git.rs index 996b6e5bd07..ce7585bb801 100644 --- a/src/git.rs +++ b/src/git.rs @@ -9,9 +9,10 @@ use git2; use serde_json; use app::App; -use dependency::Kind; use util::{internal, CargoResult}; +use models::Kind; + #[derive(Serialize, Deserialize, Debug)] pub struct Crate { pub name: String, diff --git a/src/krate/downloads.rs b/src/krate/downloads.rs index 1ab364db67f..c5d55dca7e4 100644 --- a/src/krate/downloads.rs +++ b/src/krate/downloads.rs @@ -10,12 +10,13 @@ use conduit_router::RequestParams; use diesel::prelude::*; use db::RequestTransaction; -use download::{EncodableVersionDownload, VersionDownload}; -use schema::*; use util::{CargoResult, RequestUtils}; -use Version; -use super::{to_char, Crate}; +use views::EncodableVersionDownload; +use models::{Crate, Version, VersionDownload}; +use schema::*; + +use super::to_char; /// Handles the `GET /crates/:crate_id/downloads` route. pub fn downloads(req: &mut Request) -> CargoResult { diff --git a/src/krate/follow.rs b/src/krate/follow.rs index 879e361b61b..a68936164c4 100644 --- a/src/krate/follow.rs +++ b/src/krate/follow.rs @@ -7,11 +7,11 @@ use diesel::prelude::*; use diesel; use db::RequestTransaction; -use schema::*; use user::RequestUser; use util::{CargoResult, RequestUtils}; -use super::{Crate, Follow}; +use models::{Crate, Follow}; +use schema::*; fn follow_target(req: &mut Request) -> CargoResult { let user = req.user()?; diff --git a/src/krate/metadata.rs b/src/krate/metadata.rs index 7efc4669864..d674fea8e4c 100644 --- a/src/krate/metadata.rs +++ b/src/krate/metadata.rs @@ -9,16 +9,15 @@ use conduit_router::RequestParams; use diesel::prelude::*; use app::RequestApp; -use category::{CrateCategory, EncodableCategory}; use db::RequestTransaction; -use dependency::EncodableDependency; -use keyword::{CrateKeyword, EncodableKeyword}; -use schema::*; use util::{human, CargoResult, RequestUtils}; -use version::EncodableVersion; -use {Category, Keyword, Version}; -use super::{Crate, CrateDownload, EncodableCrate, ALL_COLUMNS}; +use views::{EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, + EncodableVersion}; +use models::{Category, Crate, CrateCategory, CrateDownload, CrateKeyword, Keyword, Version}; +use schema::*; + +use super::ALL_COLUMNS; /// Handles the `GET /summary` route. pub fn summary(req: &mut Request) -> CargoResult { diff --git a/src/krate/mod.rs b/src/krate/mod.rs index 75d228d599e..a304d19e91b 100644 --- a/src/krate/mod.rs +++ b/src/krate/mod.rs @@ -1,6 +1,3 @@ -#[allow(unused_imports)] // TODO: Remove when rustc 1.23 is stable -use std::ascii::AsciiExt; - use chrono::{NaiveDate, NaiveDateTime}; use diesel::associations::Identifiable; use diesel::prelude::*; @@ -10,14 +7,14 @@ use semver; use url::Url; use app::App; -use badge::EncodableBadge; -use crate_owner_invitation::NewCrateOwnerInvitation; -use dependency::ReverseDependency; -use owner::{CrateOwner, Owner, OwnerKind}; -use schema::*; use util::{human, CargoResult}; + +use views::EncodableBadge; +use models::{Badge, Category, CrateOwner, Keyword, NewCrateOwnerInvitation, Owner, OwnerKind, + ReverseDependency, User, Version}; + +use schema::*; use with_count::*; -use {Badge, Category, Keyword, User, Version}; pub mod search; pub mod publish; @@ -560,6 +557,7 @@ mod tests { use super::*; use chrono::NaiveDate; use serde_json; + use models::Crate; #[test] fn documentation_blacklist_no_url_provided() { diff --git a/src/krate/owners.rs b/src/krate/owners.rs index 36e9ee595bd..d3f25c1340f 100644 --- a/src/krate/owners.rs +++ b/src/krate/owners.rs @@ -7,12 +7,12 @@ use serde_json; use app::RequestApp; use db::RequestTransaction; -use owner::{rights, EncodableOwner, Owner, Rights, Team}; +use owner::rights; use user::RequestUser; use util::{human, CargoResult, RequestUtils}; -use User; -use super::Crate; +use views::EncodableOwner; +use models::{Crate, Owner, Rights, Team, User}; /// Handles the `GET /crates/:crate_id/owners` route. pub fn owners(req: &mut Request) -> CargoResult { diff --git a/src/krate/publish.rs b/src/krate/publish.rs index 44edb9408b8..3fd760147a1 100644 --- a/src/krate/publish.rs +++ b/src/krate/publish.rs @@ -13,16 +13,15 @@ use app::RequestApp; use db::RequestTransaction; use dependency; use git; -use owner::{rights, Rights}; +use owner::rights; use render; -use upload; use user::RequestUser; use util::{read_fill, read_le_u32}; use util::{human, internal, CargoResult, ChainError, RequestUtils}; -use version::NewVersion; -use {Badge, Category, Keyword, User}; -use super::{EncodableCrate, NewCrate}; +use views::EncodableCrate; +use views::EncodableCrateUpload; +use models::{Badge, Category, Keyword, NewCrate, NewVersion, Rights, User}; /// Handles the `PUT /crates/new` route. /// Used by `cargo publish` to publish a new crate or to publish a new version of an @@ -193,7 +192,7 @@ pub fn publish(req: &mut Request) -> CargoResult { /// This function parses the JSON headers to interpret the data and validates /// the data during and after the parsing. Returns crate metadata and user /// information. -fn parse_new_headers(req: &mut Request) -> CargoResult<(upload::NewCrate, User)> { +fn parse_new_headers(req: &mut Request) -> CargoResult<(EncodableCrateUpload, User)> { // Read the json upload request let amt = u64::from(read_le_u32(req.body())?); let max = req.app().config.max_upload_size; @@ -203,7 +202,7 @@ fn parse_new_headers(req: &mut Request) -> CargoResult<(upload::NewCrate, User)> let mut json = vec![0; amt as usize]; read_fill(req.body(), &mut json)?; let json = String::from_utf8(json).map_err(|_| human("json body was not valid utf-8"))?; - let new: upload::NewCrate = serde_json::from_str(&json) + let new: EncodableCrateUpload = serde_json::from_str(&json) .map_err(|e| human(&format_args!("invalid upload request: {}", e)))?; // Make sure required fields are provided diff --git a/src/krate/search.rs b/src/krate/search.rs index 66a18111628..00f1b99c80a 100644 --- a/src/krate/search.rs +++ b/src/krate/search.rs @@ -5,14 +5,15 @@ use diesel::prelude::*; use diesel_full_text_search::*; use db::RequestTransaction; -use owner::OwnerKind; -use pagination::Paginate; -use schema::*; +use controllers::helpers::Paginate; use user::RequestUser; use util::{CargoResult, RequestUtils}; -use {Badge, Version}; -use super::{canon_crate_name, Crate, EncodableCrate, ALL_COLUMNS}; +use views::EncodableCrate; +use models::{Badge, Crate, OwnerKind, Version}; +use schema::*; + +use super::{canon_crate_name, ALL_COLUMNS}; /// Handles the `GET /crates` route. /// Returns a list of crates. Called in a variety of scenarios in the diff --git a/src/lib.rs b/src/lib.rs index 10e1dc99461..a89502d0bf5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,15 +51,7 @@ extern crate conduit_static; extern crate cookie; pub use app::App; -pub use self::badge::Badge; -pub use self::category::Category; pub use config::Config; -pub use self::dependency::Dependency; -pub use self::download::VersionDownload; -pub use self::keyword::Keyword; -pub use self::krate::Crate; -pub use self::user::User; -pub use self::version::Version; pub use self::uploaders::{Bomb, Uploader}; use std::sync::Arc; @@ -73,7 +65,6 @@ use util::{R404, C, R}; pub mod app; pub mod badge; pub mod boot; -pub mod category; pub mod config; pub mod crate_owner_invitation; pub mod db; @@ -83,13 +74,11 @@ pub mod download; pub mod git; pub mod github; pub mod http; -pub mod keyword; pub mod krate; pub mod owner; pub mod render; pub mod schema; pub mod token; -pub mod upload; pub mod uploaders; pub mod user; pub mod util; @@ -97,8 +86,11 @@ pub mod version; pub mod email; pub mod site_metadata; +pub mod controllers; +pub mod models; +pub mod views; + mod local_upload; -mod pagination; mod with_count; /// Used for setting different values depending on whether the app is being run in production, @@ -192,11 +184,11 @@ pub fn middleware(app: Arc) -> MiddlewareBuilder { "/crates/:crate_id/reverse_dependencies", C(krate::metadata::reverse_dependencies), ); - api_router.get("/keywords", C(keyword::index)); - api_router.get("/keywords/:keyword_id", C(keyword::show)); - api_router.get("/categories", C(category::index)); - api_router.get("/categories/:category_id", C(category::show)); - api_router.get("/category_slugs", C(category::slugs)); + api_router.get("/keywords", C(controllers::keyword::index)); + api_router.get("/keywords/:keyword_id", C(controllers::keyword::show)); + api_router.get("/categories", C(controllers::category::index)); + api_router.get("/categories/:category_id", C(controllers::category::show)); + api_router.get("/category_slugs", C(controllers::category::slugs)); api_router.get("/users/:user_id", C(user::show)); api_router.put("/users/:user_id", C(user::update_user)); api_router.get("/users/:user_id/stats", C(user::stats)); diff --git a/src/category.rs b/src/models/category.rs similarity index 68% rename from src/category.rs rename to src/models/category.rs index 2523010f6c9..60c41f56978 100644 --- a/src/category.rs +++ b/src/models/category.rs @@ -1,12 +1,9 @@ use chrono::NaiveDateTime; -use conduit::{Request, Response}; -use conduit_router::RequestParams; use diesel::*; -use Crate; -use db::RequestTransaction; +use models::Crate; use schema::*; -use util::{CargoResult, RequestUtils}; +use views::EncodableCategory; #[derive(Clone, Identifiable, Queryable, QueryableByName, Debug)] #[table_name = "categories"] @@ -29,27 +26,6 @@ pub struct CrateCategory { category_id: i32, } -#[derive(Serialize, Deserialize, Debug)] -pub struct EncodableCategory { - pub id: String, - pub category: String, - pub slug: String, - pub description: String, - #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, - pub crates_cnt: i32, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct EncodableCategoryWithSubcategories { - pub id: String, - pub category: String, - pub slug: String, - pub description: String, - #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, - pub crates_cnt: i32, - pub subcategories: Vec, -} - impl Category { pub fn encodable(self) -> EncodableCategory { let Category { @@ -179,97 +155,11 @@ impl<'a> NewCategory<'a> { } } -/// Handles the `GET /categories` route. -pub fn index(req: &mut Request) -> CargoResult { - let conn = req.db_conn()?; - let (offset, limit) = req.pagination(10, 100)?; - let query = req.query(); - let sort = query.get("sort").map_or("alpha", String::as_str); - - let categories = Category::toplevel(&conn, sort, limit, offset)?; - let categories = categories.into_iter().map(Category::encodable).collect(); - - // Query for the total count of categories - let total = Category::count_toplevel(&conn)?; - - #[derive(Serialize)] - struct R { - categories: Vec, - meta: Meta, - } - #[derive(Serialize)] - struct Meta { - total: i64, - } - - Ok(req.json(&R { - categories: categories, - meta: Meta { total: total }, - })) -} - -/// Handles the `GET /categories/:category_id` route. -pub fn show(req: &mut Request) -> CargoResult { - let slug = &req.params()["category_id"]; - let conn = req.db_conn()?; - let cat = categories::table - .filter(categories::slug.eq(::lower(slug))) - .first::(&*conn)?; - let subcats = cat.subcategories(&conn)? - .into_iter() - .map(Category::encodable) - .collect(); - - let cat = cat.encodable(); - let cat_with_subcats = EncodableCategoryWithSubcategories { - id: cat.id, - category: cat.category, - slug: cat.slug, - description: cat.description, - created_at: cat.created_at, - crates_cnt: cat.crates_cnt, - subcategories: subcats, - }; - - #[derive(Serialize)] - struct R { - category: EncodableCategoryWithSubcategories, - } - Ok(req.json(&R { - category: cat_with_subcats, - })) -} - -/// Handles the `GET /category_slugs` route. -pub fn slugs(req: &mut Request) -> CargoResult { - let conn = req.db_conn()?; - let slugs = categories::table - .select((categories::slug, categories::slug)) - .order(categories::slug) - .load(&*conn)?; - - #[derive(Serialize, Queryable)] - struct Slug { - id: String, - slug: String, - } - - #[derive(Serialize)] - struct R { - category_slugs: Vec, - } - Ok(req.json(&R { - category_slugs: slugs, - })) -} - #[cfg(test)] mod tests { use super::*; - use chrono::NaiveDate; use diesel::connection::SimpleConnection; use dotenv::dotenv; - use serde_json; use std::env; fn pg_connection() -> PgConnection { @@ -400,42 +290,4 @@ mod tests { let expected = vec![("Cat 3".to_string(), 6), ("Cat 1".to_string(), 3)]; assert_eq!(expected, categories); } - - #[test] - fn category_dates_serializes_to_rfc3339() { - let cat = EncodableCategory { - id: "".to_string(), - category: "".to_string(), - slug: "".to_string(), - description: "".to_string(), - crates_cnt: 1, - created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), - }; - let json = serde_json::to_string(&cat).unwrap(); - assert!( - json.as_str() - .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) - .is_some() - ); - } - - #[test] - fn category_with_sub_dates_serializes_to_rfc3339() { - let cat = EncodableCategoryWithSubcategories { - id: "".to_string(), - category: "".to_string(), - slug: "".to_string(), - description: "".to_string(), - crates_cnt: 1, - created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), - subcategories: vec![], - }; - let json = serde_json::to_string(&cat).unwrap(); - assert!( - json.as_str() - .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) - .is_some() - ); - } - } diff --git a/src/keyword.rs b/src/models/keyword.rs similarity index 61% rename from src/keyword.rs rename to src/models/keyword.rs index 82818ec94cf..80b12882669 100644 --- a/src/keyword.rs +++ b/src/models/keyword.rs @@ -1,17 +1,10 @@ -#[allow(unused_imports)] // TODO: Remove when rustc 1.23 is stable -use std::ascii::AsciiExt; - use chrono::NaiveDateTime; -use conduit::{Request, Response}; -use conduit_router::RequestParams; use diesel::prelude::*; use diesel; -use Crate; -use db::RequestTransaction; -use pagination::Paginate; +use models::Crate; use schema::*; -use util::{CargoResult, RequestUtils}; +use views::EncodableKeyword; #[derive(Clone, Identifiable, Queryable, Debug)] pub struct Keyword { @@ -31,14 +24,6 @@ pub struct CrateKeyword { keyword_id: i32, } -#[derive(Serialize, Deserialize, Debug)] -pub struct EncodableKeyword { - pub id: String, - pub keyword: String, - #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, - pub crates_cnt: i32, -} - impl Keyword { pub fn find_by_keyword(conn: &PgConnection, name: &str) -> QueryResult { keywords::table @@ -109,71 +94,12 @@ impl Keyword { } } -/// Handles the `GET /keywords` route. -pub fn index(req: &mut Request) -> CargoResult { - use schema::keywords; - - let conn = req.db_conn()?; - let (offset, limit) = req.pagination(10, 100)?; - let query = req.query(); - let sort = query.get("sort").map(|s| &s[..]).unwrap_or("alpha"); - - let mut query = keywords::table.into_boxed(); - - if sort == "crates" { - query = query.order(keywords::crates_cnt.desc()); - } else { - query = query.order(keywords::keyword.asc()); - } - - let data = query - .paginate(limit, offset) - .load::<(Keyword, i64)>(&*conn)?; - let total = data.get(0).map(|&(_, t)| t).unwrap_or(0); - let kws = data.into_iter() - .map(|(k, _)| k.encodable()) - .collect::>(); - - #[derive(Serialize)] - struct R { - keywords: Vec, - meta: Meta, - } - #[derive(Serialize)] - struct Meta { - total: i64, - } - - Ok(req.json(&R { - keywords: kws, - meta: Meta { total: total }, - })) -} - -/// Handles the `GET /keywords/:keyword_id` route. -pub fn show(req: &mut Request) -> CargoResult { - let name = &req.params()["keyword_id"]; - let conn = req.db_conn()?; - - let kw = Keyword::find_by_keyword(&conn, name)?; - - #[derive(Serialize)] - struct R { - keyword: EncodableKeyword, - } - Ok(req.json(&R { - keyword: kw.encodable(), - })) -} - #[cfg(test)] mod tests { use super::*; - use chrono::NaiveDate; use diesel; use diesel::connection::SimpleConnection; use dotenv::dotenv; - use serde_json; use std::env; fn pg_connection() -> PgConnection { @@ -201,21 +127,4 @@ mod tests { assert_eq!(associated.len(), 1); assert_eq!(associated.first().unwrap().keyword, "no"); } - - #[test] - fn keyword_serializes_to_rfc3339() { - let key = EncodableKeyword { - id: "".to_string(), - keyword: "".to_string(), - created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), - crates_cnt: 0, - }; - let json = serde_json::to_string(&key).unwrap(); - assert!( - json.as_str() - .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) - .is_some() - ); - } - } diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 00000000000..5e0aefb024a --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,14 @@ +pub use badge::{Badge, MaintenanceStatus}; +pub use self::category::{Category, CrateCategory, NewCategory}; +pub use crate_owner_invitation::NewCrateOwnerInvitation; +pub use dependency::{Dependency, Kind, ReverseDependency}; +pub use download::VersionDownload; +pub use self::keyword::{CrateKeyword, Keyword}; +pub use krate::{Crate, CrateDownload, Follow, NewCrate}; +pub use owner::{CrateOwner, NewTeam, Owner, OwnerKind, Rights, Team}; +pub use user::{Email, NewUser, User}; +pub use token::ApiToken; +pub use version::{NewVersion, Version}; + +mod category; +mod keyword; diff --git a/src/owner.rs b/src/owner.rs index b966bbba547..c992f4cfaca 100644 --- a/src/owner.rs +++ b/src/owner.rs @@ -2,9 +2,10 @@ use diesel::prelude::*; use app::App; use github; -use schema::*; use util::{human, CargoResult}; -use {Crate, User}; + +use models::{Crate, User}; +use schema::*; #[derive(Insertable, Associations, Identifiable, Debug, Clone, Copy)] #[belongs_to(Crate)] diff --git a/src/tests/all.rs b/src/tests/all.rs index 6e0fb4fa023..0ad27ffa448 100644 --- a/src/tests/all.rs +++ b/src/tests/all.rs @@ -29,16 +29,8 @@ use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; use cargo_registry::app::App; -use cargo_registry::category::NewCategory; -use cargo_registry::keyword::Keyword; -use cargo_registry::krate::{CrateDownload, EncodableCrate, NewCrate}; -use cargo_registry::schema::*; -use cargo_registry::upload as u; -use cargo_registry::user::NewUser; -use cargo_registry::owner::{CrateOwner, NewTeam, Team}; -use cargo_registry::version::NewVersion; use cargo_registry::user::AuthenticationSource; -use cargo_registry::{Crate, Dependency, Replica, User, Version}; +use cargo_registry::Replica; use chrono::Utc; use conduit::{Method, Request}; use conduit_test::MockRequest; @@ -46,6 +38,14 @@ use diesel::prelude::*; use flate2::Compression; use flate2::write::GzEncoder; +pub use cargo_registry::{models, schema, views}; + +use views::EncodableCrate; +use views::krate_publish as u; +use models::{Crate, CrateDownload, CrateOwner, Dependency, Keyword, Team, User, Version}; +use models::{NewCategory, NewCrate, NewTeam, NewUser, NewVersion}; +use schema::*; + macro_rules! t { ($e:expr) => ( match $e { @@ -474,7 +474,7 @@ fn new_version(crate_id: i32, num: &str) -> NewVersion { } fn krate(name: &str) -> Crate { - cargo_registry::krate::Crate { + Crate { id: NEXT_ID.fetch_add(1, Ordering::SeqCst) as i32, name: name.to_string(), updated_at: Utc::now().naive_utc(), diff --git a/src/tests/badge.rs b/src/tests/badge.rs index e8427c78c69..7179ca7d4de 100644 --- a/src/tests/badge.rs +++ b/src/tests/badge.rs @@ -1,10 +1,10 @@ -use cargo_registry::app::App; -use cargo_registry::badge::{Badge, MaintenanceStatus}; -use cargo_registry::krate::Crate; - use std::collections::HashMap; use std::sync::Arc; +use App; + +use models::{Badge, Crate, MaintenanceStatus}; + struct BadgeRef { appveyor: Badge, appveyor_attributes: HashMap, diff --git a/src/tests/categories.rs b/src/tests/categories.rs index 57c044deaab..11d92047226 100644 --- a/src/tests/categories.rs +++ b/src/tests/categories.rs @@ -1,9 +1,10 @@ use std::env; -use cargo_registry::schema::categories; use diesel::*; use dotenv::dotenv; +use schema::categories; + const ALGORITHMS: &str = r#" [algorithms] name = "Algorithms" diff --git a/src/tests/category.rs b/src/tests/category.rs index c5338f57cb4..3f6ba813cc1 100644 --- a/src/tests/category.rs +++ b/src/tests/category.rs @@ -1,6 +1,7 @@ use conduit::{Handler, Method}; -use cargo_registry::category::{Category, EncodableCategory, EncodableCategoryWithSubcategories}; +use views::{EncodableCategory, EncodableCategoryWithSubcategories}; +use models::Category; #[derive(Deserialize)] struct CategoryList { diff --git a/src/tests/keyword.rs b/src/tests/keyword.rs index b3bc761831c..4bc14a26d47 100644 --- a/src/tests/keyword.rs +++ b/src/tests/keyword.rs @@ -3,7 +3,8 @@ use std::sync::Arc; use conduit::{Handler, Method}; use conduit_test::MockRequest; -use cargo_registry::keyword::{EncodableKeyword, Keyword}; +use views::EncodableKeyword; +use models::Keyword; #[derive(Deserialize)] struct KeywordList { diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 0fe7bbbec4a..8350aff0819 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -14,21 +14,17 @@ use git2; use semver; use serde_json; -use cargo_registry::dependency::EncodableDependency; -use cargo_registry::download::EncodableVersionDownload; use cargo_registry::git; -use cargo_registry::keyword::EncodableKeyword; -use cargo_registry::krate::{Crate, EncodableCrate, MAX_NAME_LENGTH}; - -use cargo_registry::token::ApiToken; -use cargo_registry::schema::{crates, metadata, versions}; - -use cargo_registry::upload as u; -use cargo_registry::version::EncodableVersion; -use cargo_registry::category::{Category, EncodableCategory}; +use cargo_registry::krate::MAX_NAME_LENGTH; use {CrateList, CrateMeta, GoodCrate}; +use views::{EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, + EncodableVersion, EncodableVersionDownload}; +use views::krate_publish as u; +use models::{ApiToken, Category, Crate}; +use schema::{crates, metadata, versions}; + #[derive(Deserialize)] struct VersionsList { versions: Vec, diff --git a/src/tests/owners.rs b/src/tests/owners.rs index 78ef2cd9a28..d03f04242e0 100644 --- a/src/tests/owners.rs +++ b/src/tests/owners.rs @@ -2,17 +2,14 @@ use std::sync::Arc; use {CrateList, GoodCrate}; -use cargo_registry::owner::EncodableOwner; -use cargo_registry::user::EncodablePublicUser; -use cargo_registry::crate_owner_invitation::{EncodableCrateOwnerInvitation, InvitationResponse, - NewCrateOwnerInvitation}; -use cargo_registry::schema::crate_owner_invitations; -use cargo_registry::krate::Crate; - use conduit::{Handler, Method}; use diesel; use diesel::prelude::*; +use views::{EncodableCrateOwnerInvitation, EncodableOwner, EncodablePublicUser, InvitationResponse}; +use models::{Crate, NewCrateOwnerInvitation}; +use schema::crate_owner_invitations; + #[derive(Deserialize)] struct TeamResponse { teams: Vec, diff --git a/src/tests/record.rs b/src/tests/record.rs index 5b0c5fdd6ab..399e3386c07 100644 --- a/src/tests/record.rs +++ b/src/tests/record.rs @@ -8,8 +8,7 @@ use std::borrow::Cow; use std::cell::RefCell; use std::collections::HashSet; use std::env; -use std::fs::File; -use std::fs; +use std::fs::{self, File}; use std::io::prelude::*; use std::io; use std::net; @@ -19,7 +18,6 @@ use std::str; use std::sync::{Arc, Mutex, Once}; use std::thread; -use cargo_registry::user::NewUser; use curl::easy::{Easy, List}; use self::futures::{Future, Stream}; use self::futures::sync::oneshot; @@ -29,6 +27,8 @@ use self::tokio_core::reactor::Core; use self::tokio_service::Service; use serde_json; +use models::NewUser; + // A "bomb" so when the test task exists we know when to shut down // the server and fail if the subtask failed. pub struct Bomb { diff --git a/src/tests/team.rs b/src/tests/team.rs index d11927d0e88..a96430dffbb 100644 --- a/src/tests/team.rs +++ b/src/tests/team.rs @@ -1,11 +1,11 @@ use std::sync::ONCE_INIT; use conduit::{Handler, Method}; use diesel::*; - -use cargo_registry::user::NewUser; -use cargo_registry::krate::{Crate, EncodableCrate}; use record::GhUser; +use views::EncodableCrate; +use models::{Crate, NewUser}; + // Users: `crates-tester-1` and `crates-tester-2` // Passwords: ask acrichto or gankro // Teams: `crates-test-org:core`, `crates-test-org:just-for-crates-2` diff --git a/src/tests/token.rs b/src/tests/token.rs index 55ac25a8747..47c89b32ba0 100644 --- a/src/tests/token.rs +++ b/src/tests/token.rs @@ -4,7 +4,8 @@ use std::sync::Arc; use diesel::prelude::*; use conduit::{Handler, Method}; -use cargo_registry::token::{ApiToken, EncodableApiTokenWithToken}; +use views::EncodableApiTokenWithToken; +use models::ApiToken; #[derive(Deserialize)] struct DecodableApiToken { diff --git a/src/tests/user.rs b/src/tests/user.rs index a6126997243..bc80b46e9c4 100644 --- a/src/tests/user.rs +++ b/src/tests/user.rs @@ -2,14 +2,11 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use conduit::{Handler, Method}; - -use cargo_registry::token::ApiToken; -use cargo_registry::krate::EncodableCrate; -use cargo_registry::user::{Email, EncodablePrivateUser, EncodablePublicUser, NewUser, User}; -use cargo_registry::version::EncodableVersion; - use diesel::prelude::*; +use views::{EncodableCrate, EncodablePrivateUser, EncodablePublicUser, EncodableVersion}; +use models::{ApiToken, Email, NewUser, User}; + #[derive(Deserialize)] struct AuthResponse { url: String, diff --git a/src/tests/version.rs b/src/tests/version.rs index c6e777b87d8..ff7265e4e0b 100644 --- a/src/tests/version.rs +++ b/src/tests/version.rs @@ -8,8 +8,8 @@ use serde_json::Value; use conduit::{Handler, Method}; use self::diesel::prelude::*; -use cargo_registry::version::EncodableVersion; -use cargo_registry::schema::versions; +use views::EncodableVersion; +use schema::versions; #[derive(Deserialize)] struct VersionList { diff --git a/src/token.rs b/src/token.rs index 5d4f3b2dd81..5357c57c6cc 100644 --- a/src/token.rs +++ b/src/token.rs @@ -6,8 +6,10 @@ use diesel; use serde_json as json; use db::RequestTransaction; -use user::{AuthenticationSource, RequestUser, User}; +use user::{AuthenticationSource, RequestUser}; use util::{bad_request, read_fill, CargoResult, ChainError, RequestUtils}; + +use models::User; use schema::api_tokens; /// The model representing a row in the `api_tokens` database table. diff --git a/src/uploaders.rs b/src/uploaders.rs index 55806d9b414..027a7d76f59 100644 --- a/src/uploaders.rs +++ b/src/uploaders.rs @@ -1,7 +1,6 @@ use conduit::Request; use curl::easy::Easy; use flate2::read::GzDecoder; -use krate::Crate; use s3; use semver; use tar; @@ -14,6 +13,8 @@ use std::fs::{self, File}; use std::env; use std::io::{Read, Write}; +use models::Crate; + #[derive(Clone, Debug)] pub enum Uploader { /// For production usage, uploads and redirects to s3. diff --git a/src/user/middleware.rs b/src/user/middleware.rs index fa87252e374..79f03a06770 100644 --- a/src/user/middleware.rs +++ b/src/user/middleware.rs @@ -6,10 +6,11 @@ use conduit_cookie::RequestSession; use diesel::prelude::*; use db::RequestTransaction; -use schema::users; -use super::User; use util::errors::{std_error, CargoResult, ChainError, Unauthorized}; +use models::User; +use schema::users; + #[derive(Debug, Clone, Copy)] pub struct Middleware; diff --git a/src/user/mod.rs b/src/user/mod.rs index ef549939346..cdda2b58e5c 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -10,20 +10,19 @@ use serde_json; use app::RequestApp; use db::RequestTransaction; -use krate::Follow; -use pagination::Paginate; -use schema::*; +use controllers::helpers::Paginate; use util::{bad_request, human, CargoResult, RequestUtils}; -use version::EncodableVersion; -use {github, Version}; -use owner::{CrateOwner, Owner, OwnerKind}; -use krate::Crate; +use github; use email; pub use self::middleware::{AuthenticationSource, Middleware, RequestUser}; pub mod middleware; +use views::{EncodableTeam, EncodableVersion}; +use models::{Crate, CrateOwner, Follow, Owner, OwnerKind, Team, Version}; +use schema::*; + /// The model representing a row in the `users` database table. #[derive(Clone, Debug, PartialEq, Eq, Queryable, Identifiable, AsChangeset, Associations)] pub struct User { @@ -420,8 +419,6 @@ pub fn show(req: &mut Request) -> CargoResult { /// Handles the `GET /teams/:team_id` route. pub fn show_team(req: &mut Request) -> CargoResult { use self::teams::dsl::{login, teams}; - use owner::Team; - use owner::EncodableTeam; let name = &req.params()["team_id"]; let conn = req.db_conn()?; @@ -479,7 +476,6 @@ pub fn updates(req: &mut Request) -> CargoResult { /// Handles the `GET /users/:user_id/stats` route. pub fn stats(req: &mut Request) -> CargoResult { use diesel::dsl::sum; - use owner::OwnerKind; let user_id = &req.params()["user_id"].parse::().ok().unwrap(); let conn = req.db_conn()?; diff --git a/src/version/deprecated.rs b/src/version/deprecated.rs index 340261a0878..5be5c073a88 100644 --- a/src/version/deprecated.rs +++ b/src/version/deprecated.rs @@ -11,10 +11,13 @@ use diesel::prelude::*; use url; use db::RequestTransaction; -use schema::*; use util::{CargoResult, RequestUtils}; -use super::{version_and_crate, EncodableVersion, Version}; +use views::EncodableVersion; +use models::Version; +use schema::*; + +use super::version_and_crate; /// Handles the `GET /versions` route. pub fn index(req: &mut Request) -> CargoResult { diff --git a/src/version/downloads.rs b/src/version/downloads.rs index 5a65dc29400..9a317c6ca66 100644 --- a/src/version/downloads.rs +++ b/src/version/downloads.rs @@ -9,10 +9,12 @@ use diesel::prelude::*; use app::RequestApp; use db::RequestTransaction; -use download::{EncodableVersionDownload, VersionDownload}; -use schema::*; use util::{human, CargoResult, RequestUtils}; -use {Crate, Replica}; +use Replica; + +use views::EncodableVersionDownload; +use schema::*; +use models::{Crate, VersionDownload}; use super::version_and_crate; diff --git a/src/version/metadata.rs b/src/version/metadata.rs index cc0dd90ad57..5f878914d6e 100644 --- a/src/version/metadata.rs +++ b/src/version/metadata.rs @@ -8,10 +8,11 @@ use conduit::{Request, Response}; use diesel::prelude::*; use db::RequestTransaction; -use dependency::EncodableDependency; -use schema::*; use util::{CargoResult, RequestUtils}; +use views::{EncodableDependency, EncodablePublicUser}; +use schema::*; + use super::version_and_crate; /// Handles the `GET /crates/:crate_id/:version/dependencies` route. @@ -51,7 +52,7 @@ pub fn authors(req: &mut Request) -> CargoResult { // is all that is left, hear for backwards compatibility. #[derive(Serialize)] struct R { - users: Vec<::user::EncodablePublicUser>, + users: Vec, meta: Meta, } #[derive(Serialize)] diff --git a/src/version/mod.rs b/src/version/mod.rs index f1325cce34b..13b3c9c8ebe 100644 --- a/src/version/mod.rs +++ b/src/version/mod.rs @@ -9,10 +9,7 @@ use diesel::prelude::*; use semver; use serde_json; -use Crate; use db::RequestTransaction; -use dependency::Dependency; -use schema::*; use util::{human, CargoResult}; use license_exprs; @@ -21,6 +18,9 @@ pub mod downloads; pub mod metadata; pub mod yank; +use models::{Crate, Dependency}; +use schema::*; + // Queryable has a custom implementation below #[derive(Clone, Identifiable, Associations, Debug)] #[belongs_to(Crate)] @@ -58,11 +58,11 @@ pub struct EncodableVersion { pub features: HashMap>, pub yanked: bool, pub license: Option, - pub links: VersionLinks, + pub links: EncodableVersionLinks, } #[derive(Serialize, Deserialize, Debug)] -pub struct VersionLinks { +pub struct EncodableVersionLinks { pub dependencies: String, pub version_downloads: String, pub authors: String, @@ -94,7 +94,7 @@ impl Version { features: features, yanked: yanked, license: license, - links: VersionLinks { + links: EncodableVersionLinks { dependencies: format!("/api/v1/crates/{}/{}/dependencies", crate_name, num), version_downloads: format!("/api/v1/crates/{}/{}/downloads", crate_name, num), authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num), @@ -267,44 +267,3 @@ fn version_and_crate(req: &mut Request) -> CargoResult<(Version, Crate)> { })?; Ok((version, krate)) } - -#[cfg(test)] -mod tests { - use super::*; - use chrono::NaiveDate; - use serde_json; - - #[test] - fn version_serializes_to_rfc3339() { - let ver = EncodableVersion { - id: 1, - krate: "".to_string(), - num: "".to_string(), - dl_path: "".to_string(), - readme_path: "".to_string(), - updated_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), - created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 12), - downloads: 0, - features: HashMap::new(), - yanked: false, - license: None, - links: VersionLinks { - dependencies: "".to_string(), - version_downloads: "".to_string(), - authors: "".to_string(), - }, - }; - let json = serde_json::to_string(&ver).unwrap(); - assert!( - json.as_str() - .find(r#""updated_at":"2017-01-06T14:23:11+00:00""#) - .is_some() - ); - assert!( - json.as_str() - .find(r#""created_at":"2017-01-06T14:23:12+00:00""#) - .is_some() - ); - } - -} diff --git a/src/version/yank.rs b/src/version/yank.rs index 919069dd435..bc99e9e524e 100644 --- a/src/version/yank.rs +++ b/src/version/yank.rs @@ -7,12 +7,14 @@ use diesel::prelude::*; use app::RequestApp; use db::RequestTransaction; use git; -use owner::{rights, Rights}; -use schema::*; +use owner::rights; use user::RequestUser; use util::errors::CargoError; use util::{human, CargoResult, RequestUtils}; +use models::Rights; +use schema::*; + use super::version_and_crate; /// Handles the `DELETE /crates/:crate_id/:version/yank` route. diff --git a/src/upload.rs b/src/views/krate_publish.rs similarity index 98% rename from src/upload.rs rename to src/views/krate_publish.rs index 6a55fbae550..a798252f816 100644 --- a/src/upload.rs +++ b/src/views/krate_publish.rs @@ -6,10 +6,12 @@ use std::collections::HashMap; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use semver; -use dependency::Kind as DependencyKind; -use keyword::Keyword as CrateKeyword; -use krate::{Crate, MAX_NAME_LENGTH}; +use krate::MAX_NAME_LENGTH; + +use models::Keyword as CrateKeyword; +use models::Kind as DependencyKind; +use models::Crate; #[derive(Deserialize, Serialize, Debug)] pub struct NewCrate { diff --git a/src/views/mod.rs b/src/views/mod.rs new file mode 100644 index 00000000000..21e2b6e290b --- /dev/null +++ b/src/views/mod.rs @@ -0,0 +1,144 @@ +// TODO: Move all encodable types here +// For now, just reexport + +use chrono::NaiveDateTime; + +pub use badge::EncodableBadge; + +#[derive(Serialize, Deserialize, Debug)] +pub struct EncodableCategory { + pub id: String, + pub category: String, + pub slug: String, + pub description: String, + #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, + pub crates_cnt: i32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct EncodableCategoryWithSubcategories { + pub id: String, + pub category: String, + pub slug: String, + pub description: String, + #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, + pub crates_cnt: i32, + pub subcategories: Vec, +} + +pub use crate_owner_invitation::{EncodableCrateOwnerInvitation, InvitationResponse}; +pub use dependency::EncodableDependency; +pub use download::EncodableVersionDownload; + +#[derive(Serialize, Deserialize, Debug)] +pub struct EncodableKeyword { + pub id: String, + pub keyword: String, + #[serde(with = "::util::rfc3339")] pub created_at: NaiveDateTime, + pub crates_cnt: i32, +} + +pub use krate::EncodableCrate; +pub use owner::{EncodableOwner, EncodableTeam}; +pub use token::EncodableApiTokenWithToken; +pub use user::{EncodablePrivateUser, EncodablePublicUser}; +pub use version::{EncodableVersion, EncodableVersionLinks}; + +// TODO: Prefix many of these with `Encodable` then clean up the reexports +pub mod krate_publish; +pub use self::krate_publish::CrateDependency as EncodableCrateDependency; +pub use self::krate_publish::NewCrate as EncodableCrateUpload; + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use super::*; + use chrono::NaiveDate; + use serde_json; + + #[test] + fn category_dates_serializes_to_rfc3339() { + let cat = EncodableCategory { + id: "".to_string(), + category: "".to_string(), + slug: "".to_string(), + description: "".to_string(), + crates_cnt: 1, + created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), + }; + let json = serde_json::to_string(&cat).unwrap(); + assert!( + json.as_str() + .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) + .is_some() + ); + } + + #[test] + fn category_with_sub_dates_serializes_to_rfc3339() { + let cat = EncodableCategoryWithSubcategories { + id: "".to_string(), + category: "".to_string(), + slug: "".to_string(), + description: "".to_string(), + crates_cnt: 1, + created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), + subcategories: vec![], + }; + let json = serde_json::to_string(&cat).unwrap(); + assert!( + json.as_str() + .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) + .is_some() + ); + } + + #[test] + fn keyword_serializes_to_rfc3339() { + let key = EncodableKeyword { + id: "".to_string(), + keyword: "".to_string(), + created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), + crates_cnt: 0, + }; + let json = serde_json::to_string(&key).unwrap(); + assert!( + json.as_str() + .find(r#""created_at":"2017-01-06T14:23:11+00:00""#) + .is_some() + ); + } + + #[test] + fn version_serializes_to_rfc3339() { + let ver = EncodableVersion { + id: 1, + krate: "".to_string(), + num: "".to_string(), + dl_path: "".to_string(), + readme_path: "".to_string(), + updated_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 11), + created_at: NaiveDate::from_ymd(2017, 1, 6).and_hms(14, 23, 12), + downloads: 0, + features: HashMap::new(), + yanked: false, + license: None, + links: EncodableVersionLinks { + dependencies: "".to_string(), + version_downloads: "".to_string(), + authors: "".to_string(), + }, + }; + let json = serde_json::to_string(&ver).unwrap(); + assert!( + json.as_str() + .find(r#""updated_at":"2017-01-06T14:23:11+00:00""#) + .is_some() + ); + assert!( + json.as_str() + .find(r#""created_at":"2017-01-06T14:23:12+00:00""#) + .is_some() + ); + } +}