Skip to content

Commit b68f20d

Browse files
committed
Merge #398: bitreq: Re-implement json-using-serde feature
99c948c Re-implement json-using-serde feature (Tobin C. Harding) 1251953 Replace stale docs reference to minreq (Tobin C. Harding) Pull request description: This feature was removed when we gutted `minreq` after forking it. Turns out we need it to be able to use `bitreq` in `jsonrpc`. Re-implement the feature by copying code from `minreq`. Close: #393 ACKs for top commit: oleonardolima: tACK 99c948c jamillambert: ACK 99c948c Tree-SHA512: 2e22881566fd3d75e0edb70567dac0b1ed9ce66720511dcead888b2b7686fc89fb1f6bce3bd490f91997e9ade88b0665b00679c79bcef35205c6ce1117306faf
2 parents 44c839d + 99c948c commit b68f20d

File tree

7 files changed

+85
-2
lines changed

7 files changed

+85
-2
lines changed

Cargo-minimal.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ dependencies = [
165165
"rustls",
166166
"rustls-native-certs",
167167
"rustls-webpki",
168+
"serde",
169+
"serde_json",
168170
"tiny_http",
169171
"tokio",
170172
"tokio-rustls",

Cargo-recent.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ dependencies = [
165165
"rustls",
166166
"rustls-native-certs",
167167
"rustls-webpki",
168+
"serde",
169+
"serde_json",
168170
"tiny_http",
169171
"tokio",
170172
"tokio-rustls",

bitreq/Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ maintenance = { status = "experimental" }
2020
urlencoding = { version = "2.1.0", optional = true }
2121
# For the punycode feature:
2222
punycode = { version = "0.4.1", optional = true }
23+
# For the json-using-serde feature:
24+
serde = { version = "1.0.101", optional = true }
25+
serde_json = { version = "1.0.0", optional = true }
2326
# For the proxy feature:
2427
base64 = { version = "0.22", optional = true }
2528
# For the https features:
@@ -37,7 +40,7 @@ tiny_http = "0.12"
3740
chrono = "0.4.0"
3841

3942
[package.metadata.docs.rs]
40-
features = ["proxy", "https", "punycode"]
43+
features = ["json-using-serde", "proxy", "https", "punycode"]
4144

4245
[features]
4346
default = ["std"]
@@ -46,6 +49,7 @@ log = ["dep:log"]
4649
https = ["https-rustls"]
4750
https-rustls = ["rustls", "webpki-roots", "rustls-webpki"]
4851
https-rustls-probe = ["rustls", "rustls-native-certs"]
52+
json-using-serde = ["serde", "serde_json"]
4953
proxy = ["base64"]
5054
async = ["tokio", "std"]
5155
async-https = ["async", "https-rustls", "tokio-rustls"]

bitreq/src/error.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use std::{error, io};
99
// what the user might want to handle? This error doesn't really invite graceful
1010
// handling.
1111
pub enum Error {
12+
#[cfg(feature = "json-using-serde")]
13+
/// Ran into a Serde error.
14+
SerdeJsonError(serde_json::Error),
1215
/// The response body contains invalid UTF-8, so the `as_str()`
1316
/// conversion failed.
1417
InvalidUtf8InBody(str::Utf8Error),
@@ -88,6 +91,8 @@ impl fmt::Display for Error {
8891
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8992
use Error::*;
9093
match self {
94+
#[cfg(feature = "json-using-serde")]
95+
SerdeJsonError(err) => write!(f, "{}", err),
9196
#[cfg(feature = "std")]
9297
IoError(err) => write!(f, "{}", err),
9398
InvalidUtf8InBody(err) => write!(f, "{}", err),
@@ -124,6 +129,8 @@ impl error::Error for Error {
124129
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
125130
use Error::*;
126131
match self {
132+
#[cfg(feature = "json-using-serde")]
133+
SerdeJsonError(err) => Some(err),
127134
#[cfg(feature = "std")]
128135
IoError(err) => Some(err),
129136
InvalidUtf8InBody(err) => Some(err),

bitreq/src/request.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ impl Request {
173173
self
174174
}
175175

176+
/// Converts given argument to JSON and sets it as body.
177+
///
178+
/// # Errors
179+
///
180+
/// Returns
181+
/// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) if
182+
/// Serde runs into a problem when converting `body` into a
183+
/// string.
184+
#[cfg(feature = "json-using-serde")]
185+
pub fn with_json<T: serde::ser::Serialize>(mut self, body: &T) -> Result<Request, Error> {
186+
self.headers
187+
.insert("Content-Type".to_string(), "application/json; charset=UTF-8".to_string());
188+
match serde_json::to_string(&body) {
189+
Ok(json) => Ok(self.with_body(json)),
190+
Err(err) => Err(Error::SerdeJsonError(err)),
191+
}
192+
}
193+
176194
/// Sets the request timeout in seconds.
177195
pub fn with_timeout(mut self, timeout: u64) -> Request {
178196
self.timeout = Some(timeout);
@@ -297,7 +315,7 @@ impl Request {
297315
/// Returns `Err` if we run into an error while sending the
298316
/// request, or receiving/parsing the response. The specific error
299317
/// is described in the `Err`, and it can be any
300-
/// [`minreq::Error`](enum.Error.html) except
318+
/// [`bitreq::Error`](enum.Error.html) except
301319
/// [`InvalidUtf8InBody`](enum.Error.html#variant.InvalidUtf8InBody).
302320
#[cfg(feature = "async")]
303321
pub async fn send_async(self) -> Result<Response, Error> {

bitreq/src/response.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,41 @@ impl Response {
127127
/// # fn main() -> Result<(), Box<dyn std::error::Error>> { Ok(()) }
128128
/// ```
129129
pub fn into_bytes(self) -> Vec<u8> { self.body }
130+
131+
/// Converts JSON body to a `struct` using Serde.
132+
///
133+
/// # Errors
134+
///
135+
/// Returns
136+
/// [`SerdeJsonError`](enum.Error.html#variant.SerdeJsonError) if
137+
/// Serde runs into a problem, or
138+
/// [`InvalidUtf8InBody`](enum.Error.html#variant.InvalidUtf8InBody)
139+
/// if the body is not UTF-8.
140+
///
141+
/// # Example
142+
/// In case compiler cannot figure out return type you might need to declare it explicitly:
143+
///
144+
/// ```no_run
145+
/// use serde_json::Value;
146+
///
147+
/// # fn main() -> Result<(), bitreq::Error> {
148+
/// # let url_to_json_resource = "http://example.org/resource.json";
149+
/// // Value could be any type that implements Deserialize!
150+
/// let user = bitreq::get(url_to_json_resource).send()?.json::<Value>()?;
151+
/// println!("User name is '{}'", user["name"]);
152+
/// # Ok(())
153+
/// # }
154+
/// ```
155+
#[cfg(feature = "json-using-serde")]
156+
pub fn json<'a, T>(&'a self) -> Result<T, Error>
157+
where
158+
T: serde::de::Deserialize<'a>,
159+
{
160+
match serde_json::from_str(self.as_str()?) {
161+
Ok(json) => Ok(json),
162+
Err(err) => Err(Error::SerdeJsonError(err)),
163+
}
164+
}
130165
}
131166

132167
/// An HTTP response, which is loaded lazily.

bitreq/tests/main.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,21 @@ fn test_https() {
1414
assert_eq!(get_status_code(bitreq::get("https://example.com").send()), 200,);
1515
}
1616

17+
#[test]
18+
#[cfg(feature = "json-using-serde")]
19+
fn test_json_using_serde() {
20+
const JSON_SRC: &str = r#"{
21+
"str": "Json test",
22+
"num": 42
23+
}"#;
24+
25+
setup();
26+
let original_json: serde_json::Value = serde_json::from_str(JSON_SRC).unwrap();
27+
let response = bitreq::post(url("/echo")).with_json(&original_json).unwrap().send().unwrap();
28+
let actual_json: serde_json::Value = response.json().unwrap();
29+
assert_eq!(actual_json, original_json);
30+
}
31+
1732
#[test]
1833
fn test_timeout_too_low() {
1934
setup();

0 commit comments

Comments
 (0)