Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo-minimal.lock
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ dependencies = [
"rustls",
"rustls-native-certs",
"rustls-webpki",
"serde",
"serde_json",
"tiny_http",
"tokio",
"tokio-rustls",
Expand Down
2 changes: 2 additions & 0 deletions Cargo-recent.lock
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ dependencies = [
"rustls",
"rustls-native-certs",
"rustls-webpki",
"serde",
"serde_json",
"tiny_http",
"tokio",
"tokio-rustls",
Expand Down
6 changes: 5 additions & 1 deletion bitreq/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ maintenance = { status = "experimental" }
urlencoding = { version = "2.1.0", optional = true }
# For the punycode feature:
punycode = { version = "0.4.1", optional = true }
# For the json-using-serde feature:
serde = { version = "1.0.101", optional = true }
serde_json = { version = "1.0.0", optional = true }
# For the proxy feature:
base64 = { version = "0.22", optional = true }
# For the https features:
Expand All @@ -37,7 +40,7 @@ tiny_http = "0.12"
chrono = "0.4.0"

[package.metadata.docs.rs]
features = ["proxy", "https", "punycode"]
features = ["json-using-serde", "proxy", "https", "punycode"]

[features]
default = ["std"]
Expand All @@ -46,6 +49,7 @@ log = ["dep:log"]
https = ["https-rustls"]
https-rustls = ["rustls", "webpki-roots", "rustls-webpki"]
https-rustls-probe = ["rustls", "rustls-native-certs"]
json-using-serde = ["serde", "serde_json"]
proxy = ["base64"]
async = ["tokio", "std"]
async-https = ["async", "https-rustls", "tokio-rustls"]
Expand Down
7 changes: 7 additions & 0 deletions bitreq/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use std::{error, io};
// what the user might want to handle? This error doesn't really invite graceful
// handling.
pub enum Error {
#[cfg(feature = "json-using-serde")]
/// Ran into a Serde error.
SerdeJsonError(serde_json::Error),
/// The response body contains invalid UTF-8, so the `as_str()`
/// conversion failed.
InvalidUtf8InBody(str::Utf8Error),
Expand Down Expand Up @@ -88,6 +91,8 @@ impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
#[cfg(feature = "json-using-serde")]
SerdeJsonError(err) => write!(f, "{}", err),
#[cfg(feature = "std")]
IoError(err) => write!(f, "{}", err),
InvalidUtf8InBody(err) => write!(f, "{}", err),
Expand Down Expand Up @@ -124,6 +129,8 @@ impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
use Error::*;
match self {
#[cfg(feature = "json-using-serde")]
SerdeJsonError(err) => Some(err),
#[cfg(feature = "std")]
IoError(err) => Some(err),
InvalidUtf8InBody(err) => Some(err),
Expand Down
20 changes: 19 additions & 1 deletion bitreq/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ impl Request {
self
}

/// Converts given argument to JSON and sets it as body.
///
/// # Errors
///
/// Returns
/// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) if
/// Serde runs into a problem when converting `body` into a
/// string.
#[cfg(feature = "json-using-serde")]
pub fn with_json<T: serde::ser::Serialize>(mut self, body: &T) -> Result<Request, Error> {
self.headers
.insert("Content-Type".to_string(), "application/json; charset=UTF-8".to_string());
match serde_json::to_string(&body) {
Ok(json) => Ok(self.with_body(json)),
Err(err) => Err(Error::SerdeJsonError(err)),
}
}

/// Sets the request timeout in seconds.
pub fn with_timeout(mut self, timeout: u64) -> Request {
self.timeout = Some(timeout);
Expand Down Expand Up @@ -297,7 +315,7 @@ impl Request {
/// Returns `Err` if we run into an error while sending the
/// request, or receiving/parsing the response. The specific error
/// is described in the `Err`, and it can be any
/// [`minreq::Error`](enum.Error.html) except
/// [`bitreq::Error`](enum.Error.html) except
/// [`InvalidUtf8InBody`](enum.Error.html#variant.InvalidUtf8InBody).
#[cfg(feature = "async")]
pub async fn send_async(self) -> Result<Response, Error> {
Expand Down
35 changes: 35 additions & 0 deletions bitreq/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,41 @@ impl Response {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { Ok(()) }
/// ```
pub fn into_bytes(self) -> Vec<u8> { self.body }

/// Converts JSON body to a `struct` using Serde.
///
/// # Errors
///
/// Returns
/// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) if
/// Serde runs into a problem, or
/// [`InvalidUtf8InBody`](enum.Error.html#variant.InvalidUtf8InBody)
/// if the body is not UTF-8.
///
/// # Example
/// In case compiler cannot figure out return type you might need to declare it explicitly:
///
/// ```no_run
/// use serde_json::Value;
///
/// # fn main() -> Result<(), bitreq::Error> {
/// # let url_to_json_resource = "http://example.org/resource.json";
/// // Value could be any type that implements Deserialize!
/// let user = bitreq::get(url_to_json_resource).send()?.json::<Value>()?;
/// println!("User name is '{}'", user["name"]);
/// # Ok(())
/// # }
/// ```
#[cfg(feature = "json-using-serde")]
pub fn json<'a, T>(&'a self) -> Result<T, Error>
where
T: serde::de::Deserialize<'a>,
{
match serde_json::from_str(self.as_str()?) {
Ok(json) => Ok(json),
Err(err) => Err(Error::SerdeJsonError(err)),
}
}
}

/// An HTTP response, which is loaded lazily.
Expand Down
15 changes: 15 additions & 0 deletions bitreq/tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@ fn test_https() {
assert_eq!(get_status_code(bitreq::get("https://example.com").send()), 200,);
}

#[test]
#[cfg(feature = "json-using-serde")]
fn test_json_using_serde() {
const JSON_SRC: &str = r#"{
"str": "Json test",
"num": 42
}"#;

setup();
let original_json: serde_json::Value = serde_json::from_str(JSON_SRC).unwrap();
let response = bitreq::post(url("/echo")).with_json(&original_json).unwrap().send().unwrap();
let actual_json: serde_json::Value = response.json().unwrap();
assert_eq!(actual_json, original_json);
}

#[test]
fn test_timeout_too_low() {
setup();
Expand Down