From ae9dbff03a5956ed8913ffb034bc032665f035cb Mon Sep 17 00:00:00 2001 From: Salomon Popp Date: Sun, 23 May 2021 20:27:12 +0200 Subject: [PATCH] Error handling (#19) * Add error handling * Refactor error names * Order response matching by status code * Update tests * Bump version --- Cargo.lock | 9 ++++++- Cargo.toml | 3 ++- README.md | 2 -- src/main.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 73 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88998a3..70587c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,9 +262,10 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blackd-client" -version = "0.0.3" +version = "0.0.4" dependencies = [ "clap", + "custom_error", "httpmock", "minreq", "pretty_assertions", @@ -436,6 +437,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "custom_error" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6" + [[package]] name = "diff" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index bd18124..f5518af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "blackd-client" -version = "0.0.3" +version = "0.0.4" authors = ["disrupted "] edition = "2018" description = "Blazing fast Python code formatting using Black" @@ -13,6 +13,7 @@ categories = ["command-line-utilities"] [dependencies] minreq = "2.3.1" clap = "3.0.0-beta.2" +custom_error = "1.9.2" [dev-dependencies] httpmock = "0.5.8" diff --git a/README.md b/README.md index d1ae9a6..505acad 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,6 @@ Luckily there's [blackd](https://black.readthedocs.io/en/stable/usage_and_config If you're using Black (or writing Python code in general) I recommend you to check it out! -> this is very early stage and experimental. It's literally < 50 LOC I hacked together in one evening, and I'll probably improve it as I learn more about Rust. - ## Install 1. Install Black diff --git a/src/main.rs b/src/main.rs index 099a138..aadb5b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::{crate_version, AppSettings, Clap}; +use custom_error::custom_error; use std::{io, io::prelude::*}; /// Tiny HTTP client for the Black (blackd) Python code formatter @@ -6,17 +7,28 @@ use std::{io, io::prelude::*}; #[clap(version = crate_version!())] #[clap(setting = AppSettings::ColoredHelp)] struct Opts { + /// URL of blackd server #[clap(long, default_value = "http://localhost:45484")] url: String, } +custom_error! {BlackdError + Minreq{source: minreq::Error} = "{source}", + Syntax{details: String} = "Syntax Error: {details}", + Formatting{details: String} = "Formatting Error: {details}", + Unknown{status_code: i32, body: String} = "Unknown Error: {status_code}", +} + fn main() { let opts: Opts = Opts::parse(); let stdin = read_stdin(); let result = format(opts.url, stdin.unwrap()); match result { Ok(v) => print!("{}", v), - Err(e) => print!("Error formatting with blackd-client: {}", e), + Err(e) => { + eprint!("Error formatting with blackd-client: {}", e); + std::process::exit(1); + } } } @@ -30,17 +42,23 @@ fn read_stdin() -> Result> { Ok(buffer) } -fn format(url: String, stdin: String) -> Result { +fn format(url: String, stdin: String) -> Result { let resp = minreq::post(url) .with_header("X-Fast-Or-Safe", "fast") .with_header("Content-Type", "text/plain; charset=utf-8") .with_body(stdin.as_str()) .send()?; + let body = resp.as_str()?.to_string(); match resp.status_code { - 204 => Ok(stdin), // input is already well-formatted - 200 => Ok(resp.as_str()?.to_string()), // input was reformatted by Black - _ => Err(minreq::Error::Other("Error")), + 200 => Ok(body), // input was reformatted by Black + 204 => Ok(stdin), // input is already well-formatted + 400 => Err(BlackdError::Syntax { details: body }), + 500 => Err(BlackdError::Formatting { details: body }), + _ => Err(BlackdError::Unknown { + status_code: resp.status_code, + body, + }), } } @@ -87,7 +105,45 @@ mod tests { } #[test] - fn test_format_error() { + fn test_syntax_error() { + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST"); + then.status(400) + .body("Cannot parse: 1:6: print('bad syntax'))"); + }); + + let result = format(server.url(""), "print('bad syntax'))".to_string()); + + mock.assert(); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Syntax Error: Cannot parse: 1:6: print('bad syntax'))" + ); + } + + #[test] + fn test_formatting_error() { + let server = MockServer::start(); + let mock = server.mock(|when, then| { + when.method("POST"); + then.status(500) + .body("('EOF in multi-line statement', (2, 0))"); + }); + + let result = format(server.url(""), "print(('bad syntax')".to_string()); + + mock.assert(); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "Formatting Error: ('EOF in multi-line statement', (2, 0))" + ); + } + + #[test] + fn test_unknown_error() { let server = MockServer::start(); let mock = server.mock(|when, then| { when.method("POST"); @@ -98,5 +154,6 @@ mod tests { mock.assert(); assert!(result.is_err()); + assert_eq!(result.unwrap_err().to_string(), "Unknown Error: 418"); } }