diff --git a/src/controllers/keyword.rs b/src/controllers/keyword.rs index 8397612f9a1..cec61f3bf72 100644 --- a/src/controllers/keyword.rs +++ b/src/controllers/keyword.rs @@ -23,7 +23,7 @@ pub fn index(req: &mut dyn RequestExt) -> EndpointResult { let conn = req.db_conn()?; let data: Paginated = query.load(&*conn)?; let total = data.total(); - let kws = data.into_iter().map(Keyword::encodable).collect::>(); + let kws = data.into_iter().map(Keyword::into).collect::>(); #[derive(Serialize)] struct R { @@ -52,7 +52,5 @@ pub fn show(req: &mut dyn RequestExt) -> EndpointResult { struct R { keyword: EncodableKeyword, } - Ok(req.json(&R { - keyword: kw.encodable(), - })) + Ok(req.json(&R { keyword: kw.into() })) } diff --git a/src/controllers/krate/downloads.rs b/src/controllers/krate/downloads.rs index 24f29bb86e0..db9cc9848ae 100644 --- a/src/controllers/krate/downloads.rs +++ b/src/controllers/krate/downloads.rs @@ -31,7 +31,7 @@ pub fn downloads(req: &mut dyn RequestExt) -> EndpointResult { .order(version_downloads::date.asc()) .load(&*conn)? .into_iter() - .map(VersionDownload::encodable) + .map(VersionDownload::into) .collect::>(); let sum_downloads = sql::("SUM(version_downloads.downloads)"); diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index 639fff29eee..71365c1175f 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -80,7 +80,7 @@ pub fn summary(req: &mut dyn RequestExt) -> EndpointResult { .limit(10) .load(&*conn)? .into_iter() - .map(Keyword::encodable) + .map(Keyword::into) .collect(); let popular_categories = Category::toplevel(&conn, "crates", 10, 0)? @@ -178,7 +178,7 @@ pub fn show(req: &mut dyn RequestExt) -> EndpointResult { .into_iter() .map(|(v, pb, aas)| v.encodable(&krate.name, pb, aas)) .collect(), - keywords: kws.into_iter().map(Keyword::encodable).collect(), + keywords: kws.into_iter().map(Keyword::into).collect(), categories: cats.into_iter().map(Category::into).collect(), })) } diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index 4251751ef6b..def3cbcf678 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -9,11 +9,7 @@ pub fn owners(req: &mut dyn RequestExt) -> EndpointResult { let crate_name = &req.params()["crate_id"]; let conn = req.db_conn()?; let krate: Crate = Crate::by_name(crate_name).first(&*conn)?; - let owners = krate - .owners(&conn)? - .into_iter() - .map(Owner::encodable) - .collect(); + let owners = krate.owners(&conn)?.into_iter().map(Owner::into).collect(); #[derive(Serialize)] struct R { @@ -29,7 +25,7 @@ pub fn owner_team(req: &mut dyn RequestExt) -> EndpointResult { let krate: Crate = Crate::by_name(crate_name).first(&*conn)?; let owners = Team::owning(&krate, &conn)? .into_iter() - .map(Owner::encodable) + .map(Owner::into) .collect(); #[derive(Serialize)] @@ -46,7 +42,7 @@ pub fn owner_user(req: &mut dyn RequestExt) -> EndpointResult { let krate: Crate = Crate::by_name(crate_name).first(&*conn)?; let owners = User::owning(&krate, &conn)? .into_iter() - .map(Owner::encodable) + .map(Owner::into) .collect(); #[derive(Serialize)] diff --git a/src/controllers/team.rs b/src/controllers/team.rs index 0a4f352594e..c56c3e35777 100644 --- a/src/controllers/team.rs +++ b/src/controllers/team.rs @@ -16,7 +16,5 @@ pub fn show_team(req: &mut dyn RequestExt) -> EndpointResult { struct R { team: EncodableTeam, } - Ok(req.json(&R { - team: team.encodable(), - })) + Ok(req.json(&R { team: team.into() })) } diff --git a/src/controllers/token.rs b/src/controllers/token.rs index 0d272a88df2..e636be91769 100644 --- a/src/controllers/token.rs +++ b/src/controllers/token.rs @@ -87,7 +87,7 @@ pub fn new(req: &mut dyn RequestExt) -> EndpointResult { api_token: EncodableApiTokenWithToken, } Ok(req.json(&R { - api_token: api_token.encodable_with_token(), + api_token: api_token.into(), })) } diff --git a/src/controllers/user/other.rs b/src/controllers/user/other.rs index 4c669305e4a..28be3361f48 100644 --- a/src/controllers/user/other.rs +++ b/src/controllers/user/other.rs @@ -20,9 +20,7 @@ pub fn show(req: &mut dyn RequestExt) -> EndpointResult { struct R { user: EncodablePublicUser, } - Ok(req.json(&R { - user: user.encodable_public(), - })) + Ok(req.json(&R { user: user.into() })) } /// Handles the `GET /users/:user_id/stats` route. diff --git a/src/controllers/version/downloads.rs b/src/controllers/version/downloads.rs index c5e0a593511..4a00393b82d 100644 --- a/src/controllers/version/downloads.rs +++ b/src/controllers/version/downloads.rs @@ -94,7 +94,7 @@ pub fn downloads(req: &mut dyn RequestExt) -> EndpointResult { .order(version_downloads::date) .load(&*conn)? .into_iter() - .map(VersionDownload::encodable) + .map(VersionDownload::into) .collect(); #[derive(Serialize)] diff --git a/src/models/download.rs b/src/models/download.rs index 827e32b65a0..d48ec534e39 100644 --- a/src/models/download.rs +++ b/src/models/download.rs @@ -3,7 +3,6 @@ use diesel::prelude::*; use crate::models::Version; use crate::schema::version_downloads; -use crate::views::EncodableVersionDownload; #[derive(Queryable, Identifiable, Associations, Debug, Clone, Copy)] #[belongs_to(Version)] @@ -31,12 +30,4 @@ impl VersionDownload { .execute(conn)?; Ok(()) } - - pub fn encodable(self) -> EncodableVersionDownload { - EncodableVersionDownload { - version: self.version_id, - downloads: self.downloads, - date: self.date.to_string(), - } - } } diff --git a/src/models/keyword.rs b/src/models/keyword.rs index e19bf67bd5c..f11bedc3ac3 100644 --- a/src/models/keyword.rs +++ b/src/models/keyword.rs @@ -3,7 +3,6 @@ use diesel::prelude::*; use crate::models::Crate; use crate::schema::*; -use crate::views::EncodableKeyword; #[derive(Clone, Identifiable, Queryable, Debug)] pub struct Keyword { @@ -61,21 +60,6 @@ impl Keyword { && name.chars().all(|c| c.is_ascii()) } - pub fn encodable(self) -> EncodableKeyword { - let Keyword { - crates_cnt, - keyword, - created_at, - .. - } = self; - EncodableKeyword { - id: keyword.clone(), - created_at, - crates_cnt, - keyword, - } - } - pub fn update_crate(conn: &PgConnection, krate: &Crate, keywords: &[&str]) -> QueryResult<()> { conn.transaction(|| { let keywords = Keyword::find_or_create_all(conn, keywords)?; diff --git a/src/models/owner.rs b/src/models/owner.rs index 7b9bb983d9f..f7f50aeeec0 100644 --- a/src/models/owner.rs +++ b/src/models/owner.rs @@ -2,12 +2,10 @@ use diesel::pg::Pg; use diesel::prelude::*; use crate::app::App; -use crate::github; use crate::util::errors::{cargo_err, AppResult}; use crate::models::{Crate, Team, User}; use crate::schema::{crate_owners, users}; -use crate::views::EncodableOwner; #[derive(Insertable, Associations, Identifiable, Debug, Clone, Copy)] #[belongs_to(Crate)] @@ -100,43 +98,4 @@ impl Owner { Owner::Team(ref team) => team.id, } } - - pub fn encodable(self) -> EncodableOwner { - match self { - Owner::User(User { - id, - name, - gh_login, - gh_avatar, - .. - }) => { - let url = format!("https://github.com/{}", gh_login); - EncodableOwner { - id, - login: gh_login, - avatar: gh_avatar, - url: Some(url), - name, - kind: String::from("user"), - } - } - Owner::Team(Team { - id, - name, - login, - avatar, - .. - }) => { - let url = github::team_url(&login); - EncodableOwner { - id, - login, - url: Some(url), - avatar, - name, - kind: String::from("team"), - } - } - } - } } diff --git a/src/models/team.rs b/src/models/team.rs index 2f61cc365e4..347ad6bb477 100644 --- a/src/models/team.rs +++ b/src/models/team.rs @@ -1,14 +1,13 @@ use diesel::prelude::*; use crate::app::App; -use crate::github::{github_api, team_url}; +use crate::github::github_api; use crate::util::errors::{cargo_err, AppResult, NotFound}; use oauth2::AccessToken; use crate::models::{Crate, CrateOwner, Owner, OwnerKind, User}; use crate::schema::{crate_owners, teams}; -use crate::views::EncodableTeam; /// For now, just a Github Team. Can be upgraded to other teams /// later if desirable. @@ -213,25 +212,6 @@ impl Team { Ok(teams.collect()) } - - pub fn encodable(self) -> EncodableTeam { - let Team { - id, - name, - login, - avatar, - .. - } = self; - let url = team_url(&login); - - EncodableTeam { - id, - login, - name, - avatar, - url: Some(url), - } - } } fn team_with_gh_id_contains_user( diff --git a/src/models/token.rs b/src/models/token.rs index c6d7f7404ff..67ac5e92dbe 100644 --- a/src/models/token.rs +++ b/src/models/token.rs @@ -6,7 +6,6 @@ use crate::models::User; use crate::schema::api_tokens; use crate::util::errors::{AppResult, InsecurelyGeneratedTokenRevoked}; use crate::util::rfc3339; -use crate::views::EncodableApiTokenWithToken; const TOKEN_LENGTH: usize = 32; const TOKEN_PREFIX: &str = "cio"; // Crates.IO @@ -83,21 +82,6 @@ pub struct CreatedApiToken { pub plaintext: String, } -impl CreatedApiToken { - /// Converts this `CreatedApiToken` into an `EncodableApiToken` including - /// the actual token value for JSON serialization. - pub fn encodable_with_token(self) -> EncodableApiTokenWithToken { - EncodableApiTokenWithToken { - id: self.model.id, - name: self.model.name, - token: self.plaintext, - revoked: self.model.revoked, - created_at: self.model.created_at, - last_used_at: self.model.last_used_at, - } - } -} - // Use a custom implementation of Debug to hide the plaintext token. impl std::fmt::Debug for CreatedApiToken { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -111,6 +95,7 @@ impl std::fmt::Debug for CreatedApiToken { #[cfg(test)] mod tests { use super::*; + use crate::views::EncodableApiTokenWithToken; use chrono::NaiveDate; #[test] diff --git a/src/models/user.rs b/src/models/user.rs index c577af7bd31..6aa6f7c2d99 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -7,7 +7,7 @@ use crate::util::errors::AppResult; use crate::models::{ApiToken, Crate, CrateOwner, Email, NewEmail, Owner, OwnerKind, Rights}; use crate::schema::{crate_owners, emails, users}; -use crate::views::{EncodablePrivateUser, EncodablePublicUser}; +use crate::views::EncodablePrivateUser; /// The model representing a row in the `users` database table. #[derive(Clone, Debug, PartialEq, Eq, Queryable, Identifiable, AsChangeset, Associations)] @@ -203,23 +203,4 @@ impl User { .first(&*conn) .optional()?) } - - /// Converts this`User` model into an `EncodablePublicUser` for JSON serialization. - pub fn encodable_public(self) -> EncodablePublicUser { - let User { - id, - name, - gh_login, - gh_avatar, - .. - } = self; - let url = format!("https://github.com/{}", gh_login); - EncodablePublicUser { - id, - avatar: gh_avatar, - login: gh_login, - name, - url: Some(url), - } - } } diff --git a/src/models/version.rs b/src/models/version.rs index e761f0bd7e1..0b7b6ed3a70 100644 --- a/src/models/version.rs +++ b/src/models/version.rs @@ -94,12 +94,12 @@ impl Version { authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num), }, crate_size, - published_by: published_by.map(User::encodable_public), + published_by: published_by.map(User::into), audit_actions: audit_actions .into_iter() .map(|(audit_action, user)| EncodableAuditAction { action: audit_action.action.into(), - user: User::encodable_public(user), + user: user.into(), time: audit_action.time, }) .collect(), diff --git a/src/views.rs b/src/views.rs index f21b9c9bf0b..636e0a96095 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,7 +1,10 @@ use chrono::NaiveDateTime; use std::collections::HashMap; -use crate::models::{Badge, Category, DependencyKind}; +use crate::github; +use crate::models::{ + Badge, Category, CreatedApiToken, DependencyKind, Keyword, Owner, Team, User, VersionDownload, +}; use crate::util::rfc3339; #[derive(PartialEq, Debug, Serialize, Deserialize)] @@ -99,6 +102,16 @@ pub struct EncodableVersionDownload { pub date: String, } +impl From for EncodableVersionDownload { + fn from(download: VersionDownload) -> Self { + Self { + version: download.version_id, + downloads: download.downloads, + date: download.date.to_string(), + } + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct EncodableKeyword { pub id: String, @@ -108,6 +121,23 @@ pub struct EncodableKeyword { pub crates_cnt: i32, } +impl From for EncodableKeyword { + fn from(keyword: Keyword) -> Self { + let Keyword { + crates_cnt, + keyword, + created_at, + .. + } = keyword; + Self { + id: keyword.clone(), + created_at, + crates_cnt, + keyword, + } + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct EncodableCrate { pub id: String, @@ -154,6 +184,47 @@ pub struct EncodableOwner { pub avatar: Option, } +impl From for EncodableOwner { + fn from(owner: Owner) -> Self { + match owner { + Owner::User(User { + id, + name, + gh_login, + gh_avatar, + .. + }) => { + let url = format!("https://github.com/{}", gh_login); + Self { + id, + login: gh_login, + avatar: gh_avatar, + url: Some(url), + name, + kind: String::from("user"), + } + } + Owner::Team(Team { + id, + name, + login, + avatar, + .. + }) => { + let url = github::team_url(&login); + Self { + id, + login, + url: Some(url), + avatar, + name, + kind: String::from("team"), + } + } + } + } +} + #[derive(Serialize, Debug)] pub struct EncodableTeam { pub id: i32, @@ -163,6 +234,27 @@ pub struct EncodableTeam { pub url: Option, } +impl From for EncodableTeam { + fn from(team: Team) -> Self { + let Team { + id, + name, + login, + avatar, + .. + } = team; + let url = github::team_url(&login); + + EncodableTeam { + id, + login, + name, + avatar, + url: Some(url), + } + } +} + /// The serialization format for the `ApiToken` model with its token value. /// This should only be used when initially creating a new token to minimize /// the chance of token leaks. @@ -178,6 +270,19 @@ pub struct EncodableApiTokenWithToken { pub last_used_at: Option, } +impl From for EncodableApiTokenWithToken { + fn from(token: CreatedApiToken) -> Self { + EncodableApiTokenWithToken { + id: token.model.id, + name: token.model.name, + token: token.plaintext, + revoked: token.model.revoked, + created_at: token.model.created_at, + last_used_at: token.model.last_used_at, + } + } +} + #[derive(Deserialize, Serialize, Debug)] pub struct OwnedCrate { pub id: i32, @@ -217,6 +322,27 @@ pub struct EncodablePublicUser { pub url: Option, } +/// Converts a `User` model into an `EncodablePublicUser` for JSON serialization. +impl From for EncodablePublicUser { + fn from(user: User) -> Self { + let User { + id, + name, + gh_login, + gh_avatar, + .. + } = user; + let url = format!("https://github.com/{}", gh_login); + EncodablePublicUser { + id, + avatar: gh_avatar, + login: gh_login, + name, + url: Some(url), + } + } +} + #[derive(Deserialize, Serialize, Debug)] pub struct EncodableAuditAction { pub action: String,