Skip to content

Commit 2ee3842

Browse files
committed
improved error handling in core
1 parent 871e157 commit 2ee3842

File tree

3 files changed

+64
-48
lines changed

3 files changed

+64
-48
lines changed

tmc-langs-core/src/error.rs

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,48 +14,38 @@ type TokenError = oauth2::RequestTokenError<
1414

1515
#[derive(Debug, Error)]
1616
pub enum CoreError {
17-
#[error("Failed to create temporary file: {0}")]
17+
// file IO
18+
#[error("Failed to create temporary file")]
1819
TempFile(#[source] std::io::Error),
19-
#[error("Failed to create file at {0}: {1}")]
20+
#[error("Failed to create file at {0}")]
2021
FileCreate(PathBuf, #[source] std::io::Error),
21-
#[error("Failed to open file at {0}: {1}")]
22+
#[error("Failed to open file at {0}")]
2223
FileOpen(PathBuf, #[source] std::io::Error),
23-
#[error("Failed to write to file at {0}: {1}")]
24-
Write(PathBuf, #[source] std::io::Error),
25-
#[error("HTTP error for {0}: {1}")]
26-
HttpStatus(Url, StatusCode),
24+
#[error("Failed to write to file at {0}")]
25+
FileWrite(PathBuf, #[source] std::io::Error),
26+
27+
// network
28+
#[error("HTTP error {1} for {0}: {2}")]
29+
HttpStatus(Url, StatusCode, String),
2730
#[error("OAuth2 password exchange error: {0}")]
2831
Token(Box<TokenError>),
29-
#[error("OAuth2 unexpected token response {1}: {0}")]
30-
TokenParse(#[source] serde_json::error::Error, String),
32+
#[error("OAuth2 unexpected token response: {0}")]
33+
TokenParse(String, #[source] serde_json::error::Error),
34+
#[error("Failed to parse as URL: {0}")]
35+
UrlParse(String, #[source] url::ParseError),
36+
3137
#[error("Already authenticated")]
3238
AlreadyAuthenticated,
3339
#[error("Authentication required")]
3440
AuthRequired,
3541
#[error("Failed to find cache directory")]
3642
CacheDir,
3743

38-
#[error(transparent)]
39-
InvalidMethod(#[from] http::method::InvalidMethod),
40-
#[error(transparent)]
41-
InvalidHeaderName(#[from] http::header::InvalidHeaderName),
42-
#[error(transparent)]
43-
InvalidHeaderValue(#[from] http::header::InvalidHeaderValue),
44-
#[error(transparent)]
45-
InvalidStatusCode(#[from] http1::status::InvalidStatusCode),
46-
#[error(transparent)]
47-
InvalidHeaderName1(#[from] http1::header::InvalidHeaderName),
48-
#[error(transparent)]
49-
InvalidHeaderValue1(#[from] http1::header::InvalidHeaderValue),
5044
#[error(transparent)]
5145
TmcLangs(#[from] tmc_langs_util::Error),
5246
#[error(transparent)]
5347
Request(#[from] reqwest::Error),
5448
#[error(transparent)]
55-
Parse(#[from] url::ParseError),
56-
#[error(transparent)]
57-
Parse1(#[from] url1::ParseError),
58-
#[error(transparent)]
5949
Response(#[from] response::ResponseError),
6050
#[error(transparent)]
6151
ResponseErrors(#[from] response::ResponseErrors),

tmc-langs-core/src/tmc_core.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct TmcCore {
2727
#[allow(dead_code)]
2828
config_dir: PathBuf, // not used yet
2929
api_url: Url,
30-
auth_url: Url,
30+
auth_url: String,
3131
token: Option<Token>,
3232
}
3333

@@ -52,9 +52,12 @@ impl TmcCore {
5252
} else {
5353
format!("{}/", root_url)
5454
};
55-
let tmc_url = Url::parse(&root_url)?;
56-
let api_url = tmc_url.join("api/v8/")?;
57-
let auth_url = tmc_url.join("oauth/token")?;
55+
let tmc_url = Url::parse(&root_url).map_err(|e| CoreError::UrlParse(root_url, e))?;
56+
let api_url = tmc_url.join("api/v8/").expect("failed to join api/v8/");
57+
let auth_url = tmc_url
58+
.join("oauth/token")
59+
.expect("failed to join oauth/token")
60+
.to_string();
5861
Ok(Self {
5962
client: Client::new(),
6063
config_dir,
@@ -107,7 +110,8 @@ impl TmcCore {
107110
) -> impl FnOnce(oauth2::HttpRequest) -> Result<oauth2::HttpResponse> + 'a {
108111
move |req| {
109112
// convert httprequest fields
110-
let method = http::method::Method::from_bytes(req.method.as_str().as_bytes())?;
113+
let method = http::method::Method::from_bytes(req.method.as_str().as_bytes())
114+
.expect("failed to convert method");
111115
let mut headers = http::HeaderMap::new();
112116
let mut next_key = None;
113117
for (key, val) in req.headers {
@@ -124,8 +128,10 @@ impl TmcCore {
124128
continue;
125129
};
126130
let header_name =
127-
http::header::HeaderName::from_bytes(header_name.as_str().as_bytes())?;
128-
let header_value = http::header::HeaderValue::from_bytes(val.as_bytes())?;
131+
http::header::HeaderName::from_bytes(header_name.as_str().as_bytes())
132+
.expect("failed to convert header name");
133+
let header_value = http::header::HeaderValue::from_bytes(val.as_bytes())
134+
.expect("failed to convert header value");
129135
headers.insert(header_name, header_value);
130136
}
131137
let res = client
@@ -135,12 +141,15 @@ impl TmcCore {
135141
.send()?;
136142

137143
// convert response to httpresponse
138-
let status_code = http1::StatusCode::from_bytes(res.status().as_str().as_bytes())?;
144+
let status_code = http1::StatusCode::from_bytes(res.status().as_str().as_bytes())
145+
.expect("failed to convert status code");
139146
let mut headers = http1::HeaderMap::new();
140147
for (key, val) in res.headers() {
141148
let header_name =
142-
http1::header::HeaderName::from_bytes(key.as_str().as_bytes())?;
143-
let header_value = http1::header::HeaderValue::from_bytes(val.as_bytes())?;
149+
http1::header::HeaderName::from_bytes(key.as_str().as_bytes())
150+
.expect("failed to convert header name");
151+
let header_value = http1::header::HeaderValue::from_bytes(val.as_bytes())
152+
.expect("failed to convert header value");
144153
headers.insert(header_name, header_value);
145154
}
146155
let body = res.bytes()?.to_vec();
@@ -158,17 +167,23 @@ impl TmcCore {
158167
return Err(CoreError::AlreadyAuthenticated);
159168
}
160169

170+
let tail = format!("application/{}/credentials", client_name);
161171
let url = self
162172
.api_url
163-
.join(&format!("application/{}/credentials", client_name))?;
173+
.join(&tail)
174+
.map_err(|e| CoreError::UrlParse(tail, e))?;
164175
let credentials: Credentials = self.get_json_from_url(url)?;
165176

166177
log::debug!("authenticating at {}", self.auth_url);
167178
let client = BasicClient::new(
168179
ClientId::new(credentials.application_id),
169180
Some(ClientSecret::new(credentials.secret)),
170-
AuthUrl::new(self.auth_url.as_str().to_string())?, // not used in the Resource Owner Password Credentials Grant
171-
Some(TokenUrl::new(self.auth_url.as_str().to_string())?),
181+
AuthUrl::new(self.auth_url.clone())
182+
.map_err(|e| CoreError::UrlParse(self.auth_url.clone(), e))?, // not used in the Resource Owner Password Credentials Grant
183+
Some(
184+
TokenUrl::new(self.auth_url.clone())
185+
.map_err(|e| CoreError::UrlParse(self.auth_url.clone(), e))?,
186+
),
172187
);
173188

174189
let token = client
@@ -262,7 +277,7 @@ impl TmcCore {
262277
let compressed = task_executor::compress_project(submission_path)?;
263278
let mut file = NamedTempFile::new().map_err(CoreError::TempFile)?;
264279
file.write_all(&compressed)
265-
.map_err(|e| CoreError::Write(file.path().to_path_buf(), e))?;
280+
.map_err(|e| CoreError::FileWrite(file.path().to_path_buf(), e))?;
266281

267282
self.post_submission_to_paste(submission_url, file.path(), paste_message, locale)
268283
}
@@ -321,7 +336,7 @@ impl TmcCore {
321336
let compressed = task_executor::compress_project(submission_path)?;
322337
let mut file = NamedTempFile::new().map_err(CoreError::TempFile)?;
323338
file.write_all(&compressed)
324-
.map_err(|e| CoreError::Write(file.path().to_path_buf(), e))?;
339+
.map_err(|e| CoreError::FileWrite(file.path().to_path_buf(), e))?;
325340

326341
self.post_submission(submission_url, file.path(), locale)
327342
}
@@ -403,7 +418,7 @@ impl TmcCore {
403418
let compressed = task_executor::compress_project(submission_path)?;
404419
let mut file = NamedTempFile::new().map_err(CoreError::TempFile)?;
405420
file.write_all(&compressed)
406-
.map_err(|e| CoreError::Write(file.path().to_path_buf(), e))?;
421+
.map_err(|e| CoreError::FileWrite(file.path().to_path_buf(), e))?;
407422

408423
self.post_submission_for_review(submission_url, file.path(), message_for_reviewer, locale)
409424
}
@@ -430,7 +445,8 @@ impl TmcCore {
430445
return Err(CoreError::AuthRequired);
431446
}
432447

433-
let url = Url::parse(submission_url)?;
448+
let url = Url::parse(submission_url)
449+
.map_err(|e| CoreError::UrlParse(submission_url.to_string(), e))?;
434450
let res: Response<SubmissionProcessingStatus> = self.get_json_from_url(url)?;
435451
let res = res.into_result()?;
436452
Ok(res)

tmc-langs-core/src/tmc_core/api.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ impl CoreExt for ReqwestResponse {
4949
if status.is_success() {
5050
Ok(self)
5151
} else {
52-
log::error!("HTTP Error: {}", self.text()?);
53-
Err(CoreError::HttpStatus(url, status))
52+
let text = self.text().unwrap_or(String::new());
53+
Err(CoreError::HttpStatus(url, status, text))
5454
}
5555
}
5656
}
@@ -74,7 +74,10 @@ impl GetExt for RequestBuilder {
7474
impl TmcCore {
7575
// convenience function
7676
fn get_json<T: DeserializeOwned>(&self, url_tail: &str) -> Result<T> {
77-
let url = self.api_url.join(url_tail)?;
77+
let url = self
78+
.api_url
79+
.join(url_tail)
80+
.map_err(|e| CoreError::UrlParse(url_tail.to_string(), e))?;
7881
self.get_json_from_url(url)
7982
}
8083
// convenience function
@@ -89,7 +92,10 @@ impl TmcCore {
8992
}
9093

9194
fn download(&self, url_tail: &str, target: &Path) -> Result<()> {
92-
let url = self.api_url.join(&url_tail)?;
95+
let url = self
96+
.api_url
97+
.join(url_tail)
98+
.map_err(|e| CoreError::UrlParse(url_tail.to_string(), e))?;
9399

94100
// download zip
95101
let mut target_file =
@@ -615,7 +621,10 @@ impl TmcCore {
615621
review_points: &str,
616622
) -> Result<()> {
617623
let url_tail = format!("core/submissions/{}/reviews", submission_id);
618-
let url = self.api_url.join(&url_tail)?;
624+
let url = self
625+
.api_url
626+
.join(&url_tail)
627+
.map_err(|e| CoreError::UrlParse(url_tail, e))?;
619628

620629
log::debug!("posting {}", url);
621630
let res: Value = self
@@ -632,7 +641,8 @@ impl TmcCore {
632641
}
633642

634643
pub(super) fn mark_review(&self, review_update_url: String, read: bool) -> Result<()> {
635-
let url = Url::parse(&format!("{}.json", review_update_url))?;
644+
let url = format!("{}.json", review_update_url);
645+
let url = Url::parse(&url).map_err(|e| CoreError::UrlParse(url, e))?;
636646

637647
let mut form = Form::new().text("_method", "put");
638648
if read {

0 commit comments

Comments
 (0)