feat(net): mock request builder adds .with and .with_{option,result}
Some checks failed
Release Please / Release-plz (push) Successful in 1m39s
Rust / build (map[name:nightly]) (push) Has been cancelled
Rust / build (map[name:stable]) (push) Has been cancelled

Support for specifying conditional clauses fluently.
This commit is contained in:
Paul Campbell 2024-11-21 14:32:30 +00:00
parent 5169da03dc
commit ed590552c7
2 changed files with 189 additions and 3 deletions

View file

@ -146,10 +146,8 @@ mod system;
pub use result::{Error, Result};
pub use system::{MockNet, Net};
pub use system::{MockNet, Net, ReqBuilder, WithOption, WithResult};
pub use http::HeaderMap;
pub use http::Method;
pub use http::StatusCode;
pub use reqwest::Client;
pub use reqwest::Error as RequestError;

View file

@ -240,6 +240,102 @@ impl<'net> ReqBuilder<'net> {
}
}
/// Use the function to modify the request in-line.
///
/// # Example
///
/// ```rust
/// # use kxio::net::Result;
/// # async fn run() -> Result<()> {
/// let net = kxio::net::new();
/// let response= net
/// .get("http://localhost/")
/// .with(|request| add_std_headers(request))
/// .send()
/// .await?;
/// # Ok(())
/// # }
/// fn add_std_headers(request: kxio::net::ReqBuilder) -> kxio::net::ReqBuilder {
/// request
/// .header("ClientVersion", "1.23.45")
/// .header("Agent", "MyApp/1.1")
/// }
/// ````
#[must_use]
pub fn with(self, f: impl FnOnce(Self) -> Self) -> Self {
f(self)
}
/// Starts an Option clause for the supplied option.
///
/// Must be followed by [WithOption::some], [WithOption::none] or [WithOption::either]
/// to resume the with the [ReqBuilder].
///
/// # Example
///
/// ```rust
/// # use kxio::net::Result;
/// # async fn run() -> Result<()> {
/// let net = kxio::net::new();
/// let response= net
/// .get("http://localhost/")
/// .with_option(Some("value"))
/// .some(|request, value| request.header("optional-header", value))
/// .with_option(Some("value"))
/// .none(|request| request.header("special-header", "not-found"))
/// .with_option(Some("value"))
/// .either(
/// /* some */ |request, value| request.header("Setting", value),
/// /* none */ |request| request.header("Setting", "missing")
/// )
/// .send()
/// .await?;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn with_option<T>(self, option: Option<T>) -> WithOption<'net, T> {
WithOption {
req_builder: self,
option,
}
}
/// Starts a Result clause for the supplied result.
///
/// Must be followed by [WithResult::ok], [WithResult::err] or [WithResult::either]
/// to resume the with the [ReqBuilder].
///
/// # Example
///
/// ```rust
/// # use kxio::net::Result;
/// # async fn run() -> Result<()> {
/// let net = kxio::net::new();
/// let response = net
/// .get("http://localhost/")
/// .with_result::<&str, ()>(Ok("value"))
/// .ok(|request, value| request.header("good-header", value))
/// .with_result::<(), &str>(Err("value"))
/// .err(|request, err| request.header("bad-header", err))
/// .with_result::<&str, &str>(Ok("value"))
/// .either(
/// /* ok */ |request, ok| request.header("Setting", ok),
/// /* err */ |request, err| request.header("SettingError", err)
/// )
/// .send()
/// .await?;
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn with_result<T, E>(self, result: std::result::Result<T, E>) -> WithResult<'net, T, E> {
WithResult {
req_builder: self,
result,
}
}
/// Constructs the Request and sends it to the target URL, returning a
/// future Response.
///
@ -653,6 +749,98 @@ impl<'net> WhenRequest<'net, WhenBuildResponse> {
}
}
/// Option clause for building a request with [ReqBuilder].
///
/// See: [ReqBuilder::with_option].
pub struct WithOption<'net, T> {
req_builder: ReqBuilder<'net>,
option: Option<T>,
}
impl<'net, T> WithOption<'net, T> {
/// Handles when the preceeding [Option] is [Some].
///
/// The function is passed the [ReqBuilder] and the value in the [Some].
///
/// Returns the [ReqBuilder].
pub fn some(
self,
f_some: impl FnOnce(ReqBuilder<'net>, T) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.option {
Some(value) => f_some(self.req_builder, value),
None => self.req_builder,
}
}
/// Handles when the preceeding [Option] is [None].
///
/// The function is passed the [ReqBuilder].
///
/// Returns the [ReqBuilder].
pub fn none(
self,
f_none: impl FnOnce(ReqBuilder<'net>) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.option {
None => f_none(self.req_builder),
Some(_) => self.req_builder,
}
}
/// Handles the preceeding [Option].
///
/// If the [Option] is [Some], then the `f_some` function is passed the [ReqBuilder] and the value in the [Some].
///
/// If the [Option] is [None], then the `f_none` function is passed the [ReqBuilder].
///
/// Returns the [ReqBuilder].
pub fn either(
self,
f_some: impl FnOnce(ReqBuilder<'net>, T) -> ReqBuilder<'net>,
f_none: impl FnOnce(ReqBuilder<'net>) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.option {
Some(value) => f_some(self.req_builder, value),
None => f_none(self.req_builder),
}
}
}
pub struct WithResult<'net, T, E> {
req_builder: ReqBuilder<'net>,
result: std::result::Result<T, E>,
}
impl<'net, T, E> WithResult<'net, T, E> {
pub fn ok(
self,
f_ok: impl FnOnce(ReqBuilder<'net>, T) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.result {
Ok(ok) => f_ok(self.req_builder, ok),
Err(_) => self.req_builder,
}
}
pub fn err(
self,
f_err: impl FnOnce(ReqBuilder<'net>, E) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.result {
Err(err) => f_err(self.req_builder, err),
Ok(_) => self.req_builder,
}
}
pub fn either(
self,
f_ok: impl FnOnce(ReqBuilder<'net>, T) -> ReqBuilder<'net>,
f_err: impl FnOnce(ReqBuilder<'net>, E) -> ReqBuilder<'net>,
) -> ReqBuilder<'net> {
match self.result {
Ok(ok) => f_ok(self.req_builder, ok),
Err(err) => f_err(self.req_builder, err),
}
}
}
#[cfg(test)]
mod tests {
use super::*;