diff --git a/migrations/2019-05-13-192055_remove_readme_from_crates/down.sql b/migrations/2019-05-13-192055_remove_readme_from_crates/down.sql new file mode 100644 index 00000000000..714c29fb58a --- /dev/null +++ b/migrations/2019-05-13-192055_remove_readme_from_crates/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE crates ADD COLUMN readme_file VARCHAR; diff --git a/migrations/2019-05-13-192055_remove_readme_from_crates/up.sql b/migrations/2019-05-13-192055_remove_readme_from_crates/up.sql new file mode 100644 index 00000000000..9cf306b019e --- /dev/null +++ b/migrations/2019-05-13-192055_remove_readme_from_crates/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE crates DROP COLUMN readme_file; diff --git a/src/app.rs b/src/app.rs index 132858d2da5..db84bdb015f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -33,7 +33,7 @@ pub struct App { /// In production this shares a single connection pool across requests. In tests /// this is either None (in which case any attempt to create an outgoing connection /// will panic) or a `Client` configured with a per-test replay proxy. - http_client: Option, + pub(crate) http_client: Option, } impl App { diff --git a/src/background_jobs.rs b/src/background_jobs.rs index 732e914a826..5f011150824 100644 --- a/src/background_jobs.rs +++ b/src/background_jobs.rs @@ -1,8 +1,10 @@ use std::panic::AssertUnwindSafe; use std::sync::{Mutex, MutexGuard, PoisonError}; +use swirl::PerformError; use crate::db::{DieselPool, DieselPooledConn}; use crate::git::Repository; +use crate::uploaders::Uploader; use crate::util::errors::{CargoErrToStdErr, CargoResult}; impl<'a> swirl::db::BorrowedConnection<'a> for DieselPool { @@ -23,6 +25,8 @@ pub struct Environment { pub credentials: Option<(String, String)>, // FIXME: https://github.com/sfackler/r2d2/pull/70 pub connection_pool: AssertUnwindSafe, + pub uploader: Uploader, + http_client: AssertUnwindSafe, } impl Environment { @@ -30,11 +34,15 @@ impl Environment { index: Repository, credentials: Option<(String, String)>, connection_pool: DieselPool, + uploader: Uploader, + http_client: reqwest::Client, ) -> Self { Self { index: Mutex::new(index), credentials, connection_pool: AssertUnwindSafe(connection_pool), + uploader, + http_client: AssertUnwindSafe(http_client), } } @@ -44,8 +52,10 @@ impl Environment { .map(|(u, p)| (u.as_str(), p.as_str())) } - pub fn connection(&self) -> CargoResult> { - self.connection_pool.0.get().map_err(Into::into) + pub fn connection(&self) -> Result, PerformError> { + self.connection_pool + .get() + .map_err(|e| CargoErrToStdErr(e).into()) } pub fn lock_index(&self) -> CargoResult> { @@ -53,4 +63,9 @@ impl Environment { repo.reset_head()?; Ok(repo) } + + /// Returns a client for making HTTP requests to upload crate files. + pub(crate) fn http_client(&self) -> &reqwest::Client { + &self.http_client + } } diff --git a/src/bin/background-worker.rs b/src/bin/background-worker.rs index fd22def7f42..5caf09d5644 100644 --- a/src/bin/background-worker.rs +++ b/src/bin/background-worker.rs @@ -35,7 +35,13 @@ fn main() { let repository = Repository::open(&config.index_location).expect("Failed to clone index"); - let environment = Environment::new(repository, credentials, db_pool.clone()); + let environment = Environment::new( + repository, + credentials, + db_pool.clone(), + config.uploader, + reqwest::Client::new(), + ); let runner = swirl::Runner::builder(db_pool, environment) .thread_count(1) diff --git a/src/bin/render-readmes.rs b/src/bin/render-readmes.rs index 0388b8c4050..c7a6fb46174 100644 --- a/src/bin/render-readmes.rs +++ b/src/bin/render-readmes.rs @@ -114,7 +114,7 @@ fn main() { let mut tasks = Vec::with_capacity(page_size as usize); for (version, krate_name) in versions { let config = config.clone(); - version.record_readme_rendering(&conn).unwrap_or_else(|_| { + Version::record_readme_rendering(version.id, &conn).unwrap_or_else(|_| { panic!( "[{}-{}] Couldn't record rendering time", krate_name, version.num @@ -215,7 +215,6 @@ fn get_readme( .map_or("README.md", |e| &**e), manifest.package.repository.as_ref().map(|e| &**e), ) - .unwrap_or_else(|_| panic!("[{}-{}] Couldn't render README", krate_name, version.num)) }; return Some(rendered); diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index 9b5629706a7..1192ffb29a0 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -1,7 +1,6 @@ //! Functionality related to publishing a new crate or version of a crate. use hex::ToHex; -use std::collections::HashMap; use std::sync::Arc; use swirl::Job; @@ -10,8 +9,8 @@ use crate::git; use crate::models::dependency; use crate::models::{Badge, Category, Keyword, NewCrate, NewVersion, Rights, User}; use crate::render; -use crate::util::{internal, CargoError, ChainError, Maximums}; use crate::util::{read_fill, read_le_u32}; +use crate::util::{CargoError, ChainError, Maximums}; use crate::views::{EncodableCrateUpload, GoodCrate, PublishWarnings}; /// Handles the `PUT /crates/new` route. @@ -39,29 +38,6 @@ pub fn publish(req: &mut dyn Request) -> CargoResult { let (new_crate, user) = parse_new_headers(req)?; - let name = &*new_crate.name; - let vers = &*new_crate.vers; - let links = new_crate.links.clone(); - let repo = new_crate.repository.as_ref().map(|s| &**s); - let features = new_crate - .features - .iter() - .map(|(k, v)| { - ( - k[..].to_string(), - v.iter().map(|v| v[..].to_string()).collect(), - ) - }) - .collect::>>(); - let keywords = new_crate - .keywords - .as_ref() - .map(|kws| kws.iter().map(|kw| &***kw).collect()) - .unwrap_or_else(Vec::new); - - let categories = new_crate.categories.as_ref().map(|s| &s[..]).unwrap_or(&[]); - let categories: Vec<_> = categories.iter().map(|k| &***k).collect(); - let conn = app.diesel_database.get()?; let verified_email_address = user.verified_email(&conn)?; @@ -75,15 +51,34 @@ pub fn publish(req: &mut dyn Request) -> CargoResult { // Create a transaction on the database, if there are no errors, // commit the transactions to record a new or updated crate. conn.transaction(|| { + let name = new_crate.name; + let vers = &*new_crate.vers; + let links = new_crate.links; + let repo = new_crate.repository; + let features = new_crate + .features + .into_iter() + .map(|(k, v)| (k.0, v.into_iter().map(|v| v.0).collect())) + .collect(); + let keywords = new_crate + .keywords + .iter() + .map(|s| s.as_str()) + .collect::>(); + let categories = new_crate + .categories + .iter() + .map(|s| s.as_str()) + .collect::>(); + // Persist the new crate, if it doesn't already exist let persist = NewCrate { - name, + name: &name, description: new_crate.description.as_ref().map(|s| &**s), homepage: new_crate.homepage.as_ref().map(|s| &**s), documentation: new_crate.documentation.as_ref().map(|s| &**s), readme: new_crate.readme.as_ref().map(|s| &**s), - readme_file: new_crate.readme_file.as_ref().map(|s| &**s), - repository: repo, + repository: repo.as_ref().map(String::as_str), license: new_crate.license.as_ref().map(|s| &**s), max_upload_size: None, }; @@ -101,7 +96,7 @@ pub fn publish(req: &mut dyn Request) -> CargoResult { )); } - if &krate.name != name { + if krate.name != *name { return Err(human(&format_args!( "crate was previously named `{}`", krate.name @@ -163,31 +158,30 @@ pub fn publish(req: &mut dyn Request) -> CargoResult { let ignored_invalid_badges = Badge::update_crate(&conn, &krate, new_crate.badges.as_ref())?; let max_version = krate.max_version(&conn)?; - // Render the README for this crate - let readme = match new_crate.readme.as_ref() { - Some(readme) => Some(render::readme_to_html( - &**readme, - new_crate.readme_file.as_ref().map_or("README.md", |s| &**s), + if let Some(readme) = new_crate.readme { + render::render_and_upload_readme( + version.id, + readme, + new_crate + .readme_file + .unwrap_or_else(|| String::from("README.md")), repo, - )?), - None => None, - }; + ) + .enqueue(&conn) + .map_err(|e| CargoError::from_std_error(e))?; + } - // Upload the crate, return way to delete the crate from the server - // If the git commands fail below, we shouldn't keep the crate on the - // server. - let (cksum, mut crate_bomb, mut readme_bomb) = app + let cksum = app .config .uploader - .upload_crate(req, &krate, readme, maximums, vers)?; - version.record_readme_rendering(&conn)?; + .upload_crate(req, &krate, maximums, vers)?; let mut hex_cksum = String::new(); cksum.write_hex(&mut hex_cksum)?; // Register this crate in our local git repo. let git_crate = git::Crate { - name: name.to_string(), + name: name.0, vers: vers.to_string(), cksum: hex_cksum, features, @@ -197,17 +191,7 @@ pub fn publish(req: &mut dyn Request) -> CargoResult { }; git::add_crate(git_crate) .enqueue(&conn) - .map_err(|e| CargoError::from_std_error(e)) - .chain_error(|| { - internal(&format_args!( - "could not add crate `{}` to the git repo", - name - )) - })?; - - // Now that we've come this far, we're committed! - crate_bomb.path = None; - readme_bomb.path = None; + .map_err(|e| CargoError::from_std_error(e))?; // The `other` field on `PublishWarnings` was introduced to handle a temporary warning // that is no longer needed. As such, crates.io currently does not return any `other` diff --git a/src/git.rs b/src/git.rs index fcb7e008325..2a8595034bb 100644 --- a/src/git.rs +++ b/src/git.rs @@ -165,7 +165,7 @@ pub fn yank( let repo = env.lock_index().map_err(std_error_no_send)?; let dst = repo.index_file(&krate); - let conn = env.connection().map_err(std_error_no_send)?; + let conn = env.connection()?; conn.transaction(|| { let yanked_in_db = versions::table diff --git a/src/models/krate.rs b/src/models/krate.rs index 7c16ef29392..91aff10c31d 100644 --- a/src/models/krate.rs +++ b/src/models/krate.rs @@ -41,8 +41,6 @@ pub struct Crate { pub description: Option, pub homepage: Option, pub documentation: Option, - pub readme: Option, - pub readme_file: Option, pub license: Option, pub repository: Option, pub max_upload_size: Option, @@ -59,8 +57,6 @@ type AllColumns = ( crates::description, crates::homepage, crates::documentation, - crates::readme, - crates::readme_file, crates::license, crates::repository, crates::max_upload_size, @@ -75,8 +71,6 @@ pub const ALL_COLUMNS: AllColumns = ( crates::description, crates::homepage, crates::documentation, - crates::readme, - crates::readme_file, crates::license, crates::repository, crates::max_upload_size, @@ -100,7 +94,6 @@ pub struct NewCrate<'a> { pub homepage: Option<&'a str>, pub documentation: Option<&'a str>, pub readme: Option<&'a str>, - pub readme_file: Option<&'a str>, pub repository: Option<&'a str>, pub max_upload_size: Option, pub license: Option<&'a str>, diff --git a/src/models/version.rs b/src/models/version.rs index 6c52ff8eac9..4be66b41a21 100644 --- a/src/models/version.rs +++ b/src/models/version.rs @@ -99,12 +99,12 @@ impl Version { }) } - pub fn record_readme_rendering(&self, conn: &PgConnection) -> QueryResult { + pub fn record_readme_rendering(version_id_: i32, conn: &PgConnection) -> QueryResult { use crate::schema::readme_renderings::dsl::*; use diesel::dsl::now; diesel::insert_into(readme_renderings) - .values(version_id.eq(self.id)) + .values(version_id.eq(version_id_)) .on_conflict(version_id) .do_update() .set(rendered_at.eq(now)) diff --git a/src/render.rs b/src/render.rs index d20c0977a9c..3ccc7c0cca4 100644 --- a/src/render.rs +++ b/src/render.rs @@ -2,9 +2,11 @@ use ammonia::{Builder, UrlRelative}; use htmlescape::encode_minimal; use std::borrow::Cow; use std::path::Path; +use swirl::errors::PerformError; use url::Url; -use crate::util::CargoResult; +use crate::background_jobs::Environment; +use crate::models::Version; /// Context for markdown to HTML rendering. #[allow(missing_debug_implementations)] @@ -191,7 +193,7 @@ impl<'a> MarkdownRenderer<'a> { } /// Renders the given markdown to HTML using the current settings. - fn to_html(&self, text: &str) -> CargoResult { + fn to_html(&self, text: &str) -> String { let options = comrak::ComrakOptions { unsafe_: true, // The output will be sanitized with `ammonia` ext_autolink: true, @@ -203,13 +205,13 @@ impl<'a> MarkdownRenderer<'a> { ..comrak::ComrakOptions::default() }; let rendered = comrak::markdown_to_html(text, &options); - Ok(self.html_sanitizer.clean(&rendered).to_string()) + self.html_sanitizer.clean(&rendered).to_string() } } /// Renders Markdown text to sanitized HTML with a given `base_url`. /// See `readme_to_html` for the interpretation of `base_url`. -fn markdown_to_html(text: &str, base_url: Option<&str>) -> CargoResult { +fn markdown_to_html(text: &str, base_url: Option<&str>) -> String { let renderer = MarkdownRenderer::new(base_url); renderer.to_html(text) } @@ -245,14 +247,43 @@ static MARKDOWN_EXTENSIONS: [&'static str; 7] = [ /// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!"; /// let rendered = readme_to_html(text, "README.md", None)?; /// ``` -pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> CargoResult { +pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> String { let filename = filename.to_lowercase(); if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) { return markdown_to_html(text, base_url); } - Ok(encode_minimal(text).replace("\n", "
\n")) + encode_minimal(text).replace("\n", "
\n") +} + +#[swirl::background_job] +pub fn render_and_upload_readme( + env: &Environment, + version_id: i32, + text: String, + file_name: String, + base_url: Option, +) -> Result<(), PerformError> { + use crate::schema::*; + use crate::util::errors::std_error_no_send; + use diesel::prelude::*; + + let rendered = readme_to_html(&text, &file_name, base_url.as_ref().map(String::as_str)); + let conn = env.connection()?; + + conn.transaction(|| { + Version::record_readme_rendering(version_id, &conn)?; + let (crate_name, vers) = versions::table + .find(version_id) + .inner_join(crates::table) + .select((crates::name, versions::num)) + .first::<(String, String)>(&*conn)?; + env.uploader + .upload_readme(env.http_client(), &crate_name, &vers, rendered) + .map_err(std_error_no_send)?; + Ok(()) + }) } #[cfg(test)] @@ -262,14 +293,14 @@ mod tests { #[test] fn empty_text() { let text = ""; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!(result, ""); } #[test] fn text_with_script_tag() { let text = "foo_readme\n\n"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!( result, "

foo_readme

\n<script>alert(\'Hello World\')</script>\n" @@ -279,7 +310,7 @@ mod tests { #[test] fn text_with_iframe_tag() { let text = "foo_readme\n\n"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!( result, "

foo_readme

\n<iframe>alert(\'Hello World\')</iframe>\n" @@ -289,7 +320,7 @@ mod tests { #[test] fn text_with_unknown_tag() { let text = "foo_readme\n\nalert('Hello World')"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!(result, "

foo_readme

\n

alert(\'Hello World\')

\n"); } @@ -297,7 +328,7 @@ mod tests { fn text_with_inline_javascript() { let text = r#"foo_readme\n\nCrate page"#; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!( result, "

foo_readme\\n\\nCrate page

\n" @@ -309,7 +340,7 @@ mod tests { #[test] fn text_with_fancy_single_quotes() { let text = r#"wb’"#; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!(result, "

wb’

\n"); } @@ -318,14 +349,14 @@ mod tests { let code_block = r#"```rust \ println!("Hello World"); \ ```"#; - let result = markdown_to_html(code_block, None).unwrap(); + let result = markdown_to_html(code_block, None); assert!(result.contains("")); } #[test] fn text_with_forbidden_class_attribute() { let text = "

Hello World!

"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!(result, "

Hello World!

\n"); } @@ -344,7 +375,7 @@ mod tests { if extra_slash { "/" } else { "" }, ); - let result = markdown_to_html(absolute, Some(&url)).unwrap(); + let result = markdown_to_html(absolute, Some(&url)); assert_eq!( result, format!( @@ -353,7 +384,7 @@ mod tests { ) ); - let result = markdown_to_html(relative, Some(&url)).unwrap(); + let result = markdown_to_html(relative, Some(&url)); assert_eq!( result, format!( @@ -362,7 +393,7 @@ mod tests { ) ); - let result = markdown_to_html(image, Some(&url)).unwrap(); + let result = markdown_to_html(image, Some(&url)); assert_eq!( result, format!( @@ -373,7 +404,7 @@ mod tests { } } - let result = markdown_to_html(absolute, Some("https://google.com/")).unwrap(); + let result = markdown_to_html(absolute, Some("https://google.com/")); assert_eq!( result, "

hi

\n" @@ -385,7 +416,7 @@ mod tests { let readme_text = "[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap)"; let repository = "https://github.com/kbknapp/clap-rs/"; - let result = markdown_to_html(readme_text, Some(repository)).unwrap(); + let result = markdown_to_html(readme_text, Some(repository)); assert_eq!( result, @@ -397,7 +428,7 @@ mod tests { fn readme_to_html_renders_markdown() { for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] { assert_eq!( - readme_to_html("*lobster*", f, None).unwrap(), + readme_to_html("*lobster*", f, None), "

lobster

\n" ); } @@ -407,7 +438,7 @@ mod tests { fn readme_to_html_renders_other_things() { for f in &["readme.exe", "readem.org", "blah.adoc"] { assert_eq!( - readme_to_html("\n\nis my friend\n", f, None).unwrap(), + readme_to_html("\n\nis my friend\n", f, None), "<script>lobster</script>
\n
\nis my friend
\n" ); } @@ -416,7 +447,7 @@ mod tests { #[test] fn header_has_tags() { let text = "# My crate\n\nHello, world!\n"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!( result, "

My crate

\n

Hello, world!

\n" @@ -427,7 +458,7 @@ mod tests { fn manual_anchor_is_sanitized() { let text = "

My crate

\n

Hello, world!

\n"; - let result = markdown_to_html(text, None).unwrap(); + let result = markdown_to_html(text, None); assert_eq!( result, "

My crate

\n

Hello, world!

\n" diff --git a/src/schema.rs b/src/schema.rs index 75703e840d5..934c3d2e424 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -350,12 +350,6 @@ table! { /// /// (Automatically generated by Diesel.) max_upload_size -> Nullable, - /// The `readme_file` column of the `crates` table. - /// - /// Its SQL type is `Nullable`. - /// - /// (Automatically generated by Diesel.) - readme_file -> Nullable, } } diff --git a/src/tests/builders.rs b/src/tests/builders.rs index 31a51878933..1e41fb02898 100644 --- a/src/tests/builders.rs +++ b/src/tests/builders.rs @@ -477,15 +477,15 @@ impl PublishBuilder { documentation: self.doc_url, readme: self.readme, readme_file: None, - keywords: Some(u::EncodableKeywordList( + keywords: u::EncodableKeywordList( self.keywords.into_iter().map(u::EncodableKeyword).collect(), - )), - categories: Some(u::EncodableCategoryList( + ), + categories: u::EncodableCategoryList( self.categories .into_iter() .map(u::EncodableCategory) .collect(), - )), + ), license: self.license, license_file: self.license_file, repository: None, diff --git a/src/tests/util.rs b/src/tests/util.rs index d8feabf5d06..f56cea921a0 100644 --- a/src/tests/util.rs +++ b/src/tests/util.rs @@ -130,7 +130,13 @@ impl TestApp { let index_clone = WorkerRepository::open(&app.config.index_location).expect("Could not clone index"); let connection_pool = app.diesel_database.clone(); - let environment = Environment::new(index_clone, None, connection_pool.clone()); + let environment = Environment::new( + index_clone, + None, + connection_pool.clone(), + app.config.uploader.clone(), + app.http_client().clone(), + ); let runner = Runner::builder(connection_pool, environment) // We only have 1 connection in tests, so trying to run more than diff --git a/src/tests/version.rs b/src/tests/version.rs index a2bd84c2d14..c66fdf1609f 100644 --- a/src/tests/version.rs +++ b/src/tests/version.rs @@ -2,7 +2,7 @@ use crate::{ builders::{CrateBuilder, PublishBuilder, VersionBuilder}, RequestHelper, TestApp, VersionResponse, }; -use cargo_registry::{schema::versions, views::EncodableVersion}; +use cargo_registry::{models::Version, schema::versions, views::EncodableVersion}; use diesel::prelude::*; use serde_json::Value; @@ -130,8 +130,8 @@ fn record_rerendered_readme_time() { let c = CrateBuilder::new("foo_authors", user.id).expect_build(conn); let version = VersionBuilder::new("1.0.0").expect_build(c.id, user.id, conn); - version.record_readme_rendering(conn).unwrap(); - version.record_readme_rendering(conn).unwrap(); + Version::record_readme_rendering(version.id, conn).unwrap(); + Version::record_readme_rendering(version.id, conn).unwrap(); }); } diff --git a/src/uploaders.rs b/src/uploaders.rs index 80b98519cf2..c27423bafbe 100644 --- a/src/uploaders.rs +++ b/src/uploaders.rs @@ -10,7 +10,6 @@ use std::fs::{self, File}; use std::io::{Read, Write}; use std::sync::Arc; -use crate::app::App; use crate::middleware::app::RequestApp; use crate::models::Crate; @@ -112,75 +111,35 @@ impl Uploader { } } - /// Uploads a crate and its readme. Returns the checksum of the uploaded crate - /// file, and bombs for the uploaded crate and the uploaded readme. + /// Uploads a crate and returns the checksum of the uploaded crate file. pub fn upload_crate( &self, req: &mut dyn Request, krate: &Crate, - readme: Option, maximums: Maximums, vers: &semver::Version, - ) -> CargoResult<(Vec, Bomb, Bomb)> { + ) -> CargoResult> { let app = Arc::clone(req.app()); - let (crate_path, checksum) = { + let (_, checksum) = { let path = Uploader::crate_path(&krate.name, &vers.to_string()); let mut body = Vec::new(); LimitErrorReader::new(req.body(), maximums.max_upload_size).read_to_end(&mut body)?; verify_tarball(krate, vers, &body, maximums.max_unpack_size)?; self.upload(app.http_client(), &path, body, "application/x-tar")? }; - // We create the bomb for the crate file before uploading the readme so that if the - // readme upload fails, the uploaded crate file is automatically deleted. - let crate_bomb = Bomb { - app: Arc::clone(&app), - path: crate_path, - }; - let (readme_path, _) = if let Some(rendered) = readme { - let path = Uploader::readme_path(&krate.name, &vers.to_string()); - self.upload(app.http_client(), &path, rendered.into_bytes(), "text/html")? - } else { - (None, vec![]) - }; - Ok(( - checksum, - crate_bomb, - Bomb { - app: Arc::clone(&app), - path: readme_path, - }, - )) - } - - /// Deletes an uploaded file. - fn delete(&self, app: &Arc, path: &str) -> CargoResult<()> { - match *self { - Uploader::S3 { ref bucket, .. } => { - bucket.delete(app.http_client(), path)?; - Ok(()) - } - Uploader::Local => { - fs::remove_file(path)?; - Ok(()) - } - } + Ok(checksum) } -} - -// Can't derive Debug because of App. -#[allow(missing_debug_implementations)] -pub struct Bomb { - app: Arc, - pub path: Option, -} -impl Drop for Bomb { - fn drop(&mut self) { - if let Some(ref path) = self.path { - if let Err(e) = self.app.config.uploader.delete(&self.app, path) { - println!("unable to delete {}, {:?}", path, e); - } - } + pub(crate) fn upload_readme( + &self, + http_client: &reqwest::Client, + crate_name: &str, + vers: &str, + readme: String, + ) -> CargoResult<()> { + let path = Uploader::readme_path(crate_name, vers); + self.upload(http_client, &path, readme.into_bytes(), "text/html")?; + Ok(()) } } diff --git a/src/util.rs b/src/util.rs index a622deab011..383e13b0efc 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,8 +5,8 @@ use std::io::Cursor; use conduit::Response; use serde::Serialize; +pub use self::errors::ChainError; pub use self::errors::{bad_request, human, internal, internal_error, CargoError, CargoResult}; -pub use self::errors::{std_error, ChainError}; pub use self::io_util::{read_fill, read_le_u32, LimitErrorReader}; pub use self::request_helpers::*; pub use self::request_proxy::RequestProxy; diff --git a/src/util/errors.rs b/src/util/errors.rs index c948b1f5806..9903c0ba463 100644 --- a/src/util/errors.rs +++ b/src/util/errors.rs @@ -345,11 +345,11 @@ impl fmt::Display for CargoErrToStdErr { } } -pub fn std_error(e: Box) -> Box { +pub(crate) fn std_error(e: Box) -> Box { Box::new(CargoErrToStdErr(e)) } -pub fn std_error_no_send(e: Box) -> Box { +pub(crate) fn std_error_no_send(e: Box) -> Box { Box::new(CargoErrToStdErr(e)) } diff --git a/src/views/krate_publish.rs b/src/views/krate_publish.rs index 0fb3c496ce9..0169744400d 100644 --- a/src/views/krate_publish.rs +++ b/src/views/krate_publish.rs @@ -24,8 +24,10 @@ pub struct EncodableCrateUpload { pub documentation: Option, pub readme: Option, pub readme_file: Option, - pub keywords: Option, - pub categories: Option, + #[serde(default)] + pub keywords: EncodableKeywordList, + #[serde(default)] + pub categories: EncodableCategoryList, pub license: Option, pub license_file: Option, pub repository: Option, @@ -40,11 +42,11 @@ pub struct EncodableCrateName(pub String); pub struct EncodableCrateVersion(pub semver::Version); #[derive(Debug, Deref)] pub struct EncodableCrateVersionReq(pub semver::VersionReq); -#[derive(Serialize, Debug, Deref)] +#[derive(Serialize, Debug, Deref, Default)] pub struct EncodableKeywordList(pub Vec); #[derive(Serialize, Debug, Deref)] pub struct EncodableKeyword(pub String); -#[derive(Serialize, Debug, Deref)] +#[derive(Serialize, Debug, Deref, Default)] pub struct EncodableCategoryList(pub Vec); #[derive(Serialize, Deserialize, Debug, Deref)] pub struct EncodableCategory(pub String);