Skip to content

Commit b4499af

Browse files
committed
refactor(crates-io): remove anyhow
This removes the dependency `anyhow` and uses our own custom Error enum, so that crates-io consumer can access `Error::API::challenge` field.
1 parent a561e8b commit b4499af

File tree

3 files changed

+61
-49
lines changed

3 files changed

+61
-49
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/crates-io/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ name = "crates_io"
1313
path = "lib.rs"
1414

1515
[dependencies]
16-
anyhow.workspace = true
1716
curl.workspace = true
1817
percent-encoding.workspace = true
1918
serde = { workspace = true, features = ["derive"] }

crates/crates-io/lib.rs

Lines changed: 61 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use std::io::prelude::*;
77
use std::io::{Cursor, SeekFrom};
88
use std::time::Instant;
99

10-
use anyhow::{format_err, Context};
1110
use curl::easy::{Easy, List};
1211
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
1312
use serde::{Deserialize, Serialize};
@@ -127,37 +126,66 @@ struct Crates {
127126
meta: TotalCrates,
128127
}
129128

129+
/// Error returned when interacting with a registry.
130130
#[derive(Debug)]
131131
pub enum Error {
132+
/// Error from libcurl.
132133
Curl(curl::Error),
134+
135+
/// Error from seriailzing the request payload and deserialzing the
136+
/// response body (like response body didn't match expected structure).
137+
Json(serde_json::Error),
138+
139+
/// Error from IO. Mostly from reading the tarball to upload.
140+
Io(std::io::Error),
141+
142+
/// Response body was not valid utf8.
143+
Utf8(std::string::FromUtf8Error),
144+
145+
/// Error from API response containing JSON field `errors.details`.
133146
Api {
134147
code: u32,
135148
headers: Vec<String>,
136149
errors: Vec<String>,
137150
},
151+
152+
/// Error from API response which didn't have pre-programmed `errors.details`.
138153
Code {
139154
code: u32,
140155
headers: Vec<String>,
141156
body: String,
142157
},
143-
Other(anyhow::Error),
158+
159+
/// Reason why the token was invalid.
160+
InvalidToken(&'static str),
161+
162+
/// Server was unavailable and timeouted. Happened when uploading a way
163+
/// too large tarball to crates.io.
164+
Timeout(u64),
144165
}
145166

146167
impl std::error::Error for Error {
147168
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
148169
match self {
149-
Self::Curl(..) => None,
170+
Self::Curl(e) => Some(e),
171+
Self::Json(e) => Some(e),
172+
Self::Io(e) => Some(e),
173+
Self::Utf8(e) => Some(e),
150174
Self::Api { .. } => None,
151175
Self::Code { .. } => None,
152-
Self::Other(e) => Some(e.as_ref()),
176+
Self::InvalidToken(..) => None,
177+
Self::Timeout(..) => None,
153178
}
154179
}
155180
}
156181

157182
impl fmt::Display for Error {
158183
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159184
match self {
160-
Self::Curl(e) => write!(f, "{}", e),
185+
Self::Curl(e) => write!(f, "{e}"),
186+
Self::Json(e) => write!(f, "{e}"),
187+
Self::Io(e) => write!(f, "{e}"),
188+
Self::Utf8(e) => write!(f, "{e}"),
161189
Self::Api { code, errors, .. } => {
162190
f.write_str("the remote server responded with an error")?;
163191
if *code != 200 {
@@ -180,7 +208,14 @@ impl fmt::Display for Error {
180208
headers.join("\n\t"),
181209
body
182210
),
183-
Self::Other(..) => write!(f, "invalid response from server"),
211+
Self::InvalidToken(e) => write!(f, "{e}"),
212+
Self::Timeout(tarball_size) => write!(
213+
f,
214+
"Request timed out after 30 seconds. If you're trying to \
215+
upload a crate it may be too large. If the crate is under \
216+
10MB in size, you can email [email protected] for assistance.\n\
217+
Total size was {tarball_size}."
218+
),
184219
}
185220
}
186221
}
@@ -191,21 +226,21 @@ impl From<curl::Error> for Error {
191226
}
192227
}
193228

194-
impl From<anyhow::Error> for Error {
195-
fn from(error: anyhow::Error) -> Self {
196-
Self::Other(error)
229+
impl From<serde_json::Error> for Error {
230+
fn from(error: serde_json::Error) -> Self {
231+
Self::Json(error)
197232
}
198233
}
199234

200-
impl From<serde_json::Error> for Error {
201-
fn from(error: serde_json::Error) -> Self {
202-
Self::Other(error.into())
235+
impl From<std::io::Error> for Error {
236+
fn from(error: std::io::Error) -> Self {
237+
Self::Io(error)
203238
}
204239
}
205240

206-
impl From<url::ParseError> for Error {
207-
fn from(error: url::ParseError) -> Self {
208-
Self::Other(error.into())
241+
impl From<std::string::FromUtf8Error> for Error {
242+
fn from(error: std::string::FromUtf8Error) -> Self {
243+
Self::Utf8(error)
209244
}
210245
}
211246

@@ -242,12 +277,9 @@ impl Registry {
242277
}
243278

244279
fn token(&self) -> Result<&str> {
245-
let token = match self.token.as_ref() {
246-
Some(s) => s,
247-
None => {
248-
return Err(format_err!("no upload token found, please run `cargo login`").into())
249-
}
250-
};
280+
let token = self.token.as_ref().ok_or_else(|| {
281+
Error::InvalidToken("no upload token found, please run `cargo login`")
282+
})?;
251283
check_token(token)?;
252284
Ok(token)
253285
}
@@ -293,12 +325,8 @@ impl Registry {
293325
// This checks the length using seeking instead of metadata, because
294326
// on some filesystems, getting the metadata will fail because
295327
// the file was renamed in ops::package.
296-
let tarball_len = tarball
297-
.seek(SeekFrom::End(0))
298-
.with_context(|| "failed to seek tarball")?;
299-
tarball
300-
.seek(SeekFrom::Start(0))
301-
.with_context(|| "failed to seek tarball")?;
328+
let tarball_len = tarball.seek(SeekFrom::End(0))?;
329+
tarball.seek(SeekFrom::Start(0))?;
302330
let header = {
303331
let mut w = Vec::new();
304332
w.extend(&(json.len() as u32).to_le_bytes());
@@ -328,13 +356,7 @@ impl Registry {
328356
&& started.elapsed().as_secs() >= 29
329357
&& self.host_is_crates_io() =>
330358
{
331-
format_err!(
332-
"Request timed out after 30 seconds. If you're trying to \
333-
upload a crate it may be too large. If the crate is under \
334-
10MB in size, you can email [email protected] for assistance.\n\
335-
Total size was {}.",
336-
tarball_len
337-
)
359+
Error::Timeout(tarball_len)
338360
}
339361
_ => e.into(),
340362
})?;
@@ -457,14 +479,7 @@ impl Registry {
457479
handle.perform()?;
458480
}
459481

460-
let body = match String::from_utf8(body) {
461-
Ok(body) => body,
462-
Err(..) => {
463-
return Err(Error::Other(format_err!(
464-
"response body was not valid utf-8"
465-
)))
466-
}
467-
};
482+
let body = String::from_utf8(body)?;
468483
let errors = serde_json::from_str::<ApiErrorList>(&body)
469484
.ok()
470485
.map(|s| s.errors.into_iter().map(|s| s.detail).collect::<Vec<_>>());
@@ -548,7 +563,7 @@ pub fn is_url_crates_io(url: &str) -> bool {
548563
/// registries only create tokens in that format so that is as less restricted as possible.
549564
pub fn check_token(token: &str) -> Result<()> {
550565
if token.is_empty() {
551-
return Err(format_err!("please provide a non-empty token").into());
566+
return Err(Error::InvalidToken("please provide a non-empty token"));
552567
}
553568
if token.bytes().all(|b| {
554569
// This is essentially the US-ASCII limitation of
@@ -559,10 +574,9 @@ pub fn check_token(token: &str) -> Result<()> {
559574
}) {
560575
Ok(())
561576
} else {
562-
Err(format_err!(
577+
Err(Error::InvalidToken(
563578
"token contains invalid characters.\nOnly printable ISO-8859-1 characters \
564-
are allowed as it is sent in a HTTPS header."
565-
)
566-
.into())
579+
are allowed as it is sent in a HTTPS header.",
580+
))
567581
}
568582
}

0 commit comments

Comments
 (0)