use crate::{
client::GleifClient,
error::{GleifError, ResponseContent, Result},
};
use reqwest::Method;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use url::Url;
#[derive(Debug, Clone)]
pub struct GleifRequestBuilder {
client: GleifClient,
method: Method,
path: String,
query: HashMap<String, String>,
}
impl GleifRequestBuilder {
#[must_use]
pub fn new(client: GleifClient, method: Method, path: impl Into<String>) -> Self {
Self {
client,
method,
path: path.into(),
query: HashMap::new(),
}
}
#[must_use]
pub fn get_path(&self) -> &str {
&self.path
}
#[must_use]
pub fn get_query(&self) -> &HashMap<String, String> {
&self.query
}
#[must_use]
fn set_filter(mut self, field: &str, value: String) -> Self {
self.query.insert(format!("filter[{field}]"), value);
self
}
#[must_use]
pub fn filter_eq<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), value.as_ref().to_string())
}
#[must_use]
pub fn filter_not<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), format!("!{}", value.as_ref()))
}
#[must_use]
pub fn filter_in<F, I, V>(self, field: F, values: I) -> Self
where
F: AsRef<str>,
I: IntoIterator<Item = V>,
V: AsRef<str>,
{
let joined = values
.into_iter()
.map(|s| s.as_ref().to_owned())
.collect::<Vec<String>>()
.join(",");
self.set_filter(field.as_ref(), joined)
}
#[must_use]
pub fn filter_not_in<F, I, V>(self, field: F, values: I) -> Self
where
F: AsRef<str>,
I: IntoIterator<Item = V>,
V: AsRef<str>,
{
let joined = values
.into_iter()
.map(|s| s.as_ref().to_owned())
.collect::<Vec<String>>()
.join(",");
self.set_filter(field.as_ref(), format!("!{joined}"))
}
#[must_use]
pub fn filter_range<F, V>(self, field: F, min: V, max: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(
field.as_ref(),
format!("{}..{}", min.as_ref(), max.as_ref()),
)
}
#[must_use]
pub fn filter_gt<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), format!(">{}", value.as_ref()))
}
#[must_use]
pub fn filter_gte<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), format!(">={}", value.as_ref()))
}
#[must_use]
pub fn filter_lt<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), format!("<{}", value.as_ref()))
}
#[must_use]
pub fn filter_lte<F, V>(self, field: F, value: V) -> Self
where
F: AsRef<str>,
V: AsRef<str>,
{
self.set_filter(field.as_ref(), format!("<={}", value.as_ref()))
}
#[must_use]
pub fn sort<F>(mut self, field: F) -> Self
where
F: AsRef<str>,
{
self.query
.insert("sort".to_string(), field.as_ref().to_string());
self
}
#[must_use]
pub fn page_number(mut self, number: usize) -> Self {
self.query
.insert("page[number]".to_string(), number.to_string());
self
}
#[must_use]
pub fn page_size(mut self, size: usize) -> Self {
self.query
.insert("page[size]".to_string(), size.to_string());
self
}
#[must_use]
pub fn param(mut self, key: &str, value: &str) -> Self {
self.query.insert(key.to_string(), value.to_string());
self
}
fn build_url(&self) -> Result<Url> {
self.client
.base_url()
.join(&self.path)
.map_err(GleifError::UrlParseError)
}
fn build_request(&self, url: Url) -> reqwest_middleware::RequestBuilder {
self.client
.client()
.as_ref()
.request(self.method.clone(), url)
.header("Accept", "application/vnd.api+json")
.query(&self.query)
}
pub async fn send<R>(self) -> Result<R>
where
R: DeserializeOwned,
{
let url = self.build_url()?;
let req = self.build_request(url);
let resp = req.send().await.map_err(GleifError::from)?;
let status = resp.status();
let resp_text = resp.text().await?;
if !status.is_client_error() && !status.is_server_error() {
let parsed_response: R = serde_json::from_str(&resp_text).map_err(GleifError::from)?;
Ok(parsed_response)
} else {
Err(GleifError::ResponseError(ResponseContent {
status,
content: resp_text,
}))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::client::GleifClient;
use reqwest::Client as ReqwestClient;
fn test_client() -> GleifClient {
let reqwest_client = ReqwestClient::new();
GleifClient::from_reqwest_client(reqwest_client)
}
#[test]
fn test_filter_eq() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_eq("field", "value");
assert_eq!(builder.query.get("filter[field]").unwrap(), "value");
}
#[test]
fn test_filter_not() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_not("field", "value");
assert_eq!(builder.query.get("filter[field]").unwrap(), "!value");
}
#[test]
fn test_filter_in() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_in("field", ["a", "b", "c"]);
assert_eq!(builder.query.get("filter[field]").unwrap(), "a,b,c");
}
#[test]
fn test_filter_not_in() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_not_in("field", ["a", "b", "c"]);
assert_eq!(builder.query.get("filter[field]").unwrap(), "!a,b,c");
}
#[test]
fn test_filter_range() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_range("field", "min", "max");
assert_eq!(builder.query.get("filter[field]").unwrap(), "min..max");
}
#[test]
fn test_filter_gt() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_gt("field", "123");
assert_eq!(builder.query.get("filter[field]").unwrap(), ">123");
}
#[test]
fn test_filter_gte() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_gte("field", "123");
assert_eq!(builder.query.get("filter[field]").unwrap(), ">=123");
}
#[test]
fn test_filter_lt() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_lt("field", "123");
assert_eq!(builder.query.get("filter[field]").unwrap(), "<123");
}
#[test]
fn test_filter_lte() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.filter_lte("field", "123");
assert_eq!(builder.query.get("filter[field]").unwrap(), "<=123");
}
#[test]
fn test_sort_and_pagination_and_param() {
let builder = GleifRequestBuilder::new(test_client(), Method::GET, "lei-records")
.sort("field")
.page_number(2)
.page_size(10)
.param("custom", "value");
assert_eq!(builder.query.get("sort").unwrap(), "field");
assert_eq!(builder.query.get("page[number]").unwrap(), "2");
assert_eq!(builder.query.get("page[size]").unwrap(), "10");
assert_eq!(builder.query.get("custom").unwrap(), "value");
}
}