#[macro_export]
#[doc(hidden)]
macro_rules! request_builder {
(
// Struct definition with url query parameters
$(#[$struct_attrs:meta])*
pub $struct_name:ident {
$(
$(#[$parameter_attr_name:ident $($parameter_attr_values:tt)*])*
$parameter_name:ident: Option<$parameter_type:ty>
),* $(,)?
},
$method:expr,
( $url:literal $(,)? $($url_fragment_name:ident),* ),
$(
$(#[$field_attr_name:ident $($field_attr_values:tt)*])*
$field_name:ident: Option<$field_type:ty>
),* $(,)?
$((
$(#[$callback_attr_name:ident $($callback_attr_values:tt)*])*
$callback_name:ident: Option<$callback_type:ty>
)),* $(,)?
) => {
$(#[$struct_attrs])*
#[derive(Debug, Clone)]
pub struct $struct_name {
credentials: $crate::Credentials,
method: ::reqwest::Method,
url: String,
fields: Option<String>,
$($parameter_name: Option<$parameter_type>,)*
$($field_name: Option<$field_type>,)*
$($callback_name: Option<$callback_type>),*
}
impl $struct_name {
#[doc = concat!("Creates a new [`", stringify!($struct_name), "`] builder, authorized with the given [`Credentials`].")]
pub fn new( credentials: &$crate::Credentials, $($url_fragment_name: impl AsRef<str>),* ) -> $struct_name {
$struct_name {
credentials: credentials.clone(),
method: $method,
url: format!( $url, $($url_fragment_name=$url_fragment_name.as_ref().to_string()),* ),
fields: None,
$($parameter_name: None,)*
$($field_name: None,)*
$($callback_name: None),*
}
}
pub fn fields<T: AsRef<str>> ( mut self, fields: T ) -> $struct_name {
self.fields = Some( fields.as_ref().to_string() );
self
}
$(
$(#[$parameter_attr_name $($parameter_attr_values)*])*
pub fn $parameter_name<T> ( mut self, value: T ) -> $struct_name
where
T: Into<$parameter_type>
{
self.$parameter_name = Some( value.into() );
self
}
)*
$(
$(#[$field_attr_name $($field_attr_values)*])*
pub fn $field_name<T> ( mut self, value: T ) -> $struct_name
where
T: Into<$field_type>
{
self.$field_name = Some( value.into() );
self
}
)*
$(
$(#[$callback_attr_name $($callback_attr_values)*])*
pub fn $callback_name( mut self, func: $callback_type ) -> $struct_name {
self.$callback_name = Some(func);
self
}
)*
fn get_parameters( &self ) -> Vec<(String, String)> {
let mut parameters = Vec::new();
if let Some(fields) = &self.fields {
parameters.push(( "fields".into(), fields.to_string() ));
}
$(
if let Some(value) = &self.$parameter_name {
use convert_case::{Case, Casing};
let mut parameter_name = stringify!($parameter_name).from_case(Case::Snake).to_case(Case::Camel);
if ¶meter_name == "kind" {
parameter_name = "type".to_string();
}
parameters.push(( parameter_name, value.to_string() ));
}
)*
parameters
}
fn build( &self ) -> Result<::reqwest::blocking::RequestBuilder, $crate::Error> {
let parameters = self.get_parameters();
let request_url = ::reqwest::Url::parse_with_params(&self.url, parameters)?;
Ok(
::reqwest::blocking::Client::new()
.request( self.method.clone(), request_url )
.bearer_auth( self.credentials.get_access_token() )
)
}
#[allow(dead_code)] fn send( &self ) -> Result<::reqwest::blocking::Response, $crate::Error> {
let request = self.build()?;
let response = request.send()?;
if !response.status().is_success() {
return Err( response.into() );
}
Ok(response)
}
}
};
}
#[cfg(test)]
mod tests {
use reqwest::header;
use std::str::FromStr;
use reqwest::{Method, Url};
use crate::{request_builder, ErrorKind};
use crate::utils::test::INVALID_CREDENTIALS;
request_builder!(
pub TestRequest {
test_field: Option<String>,
kind: Option<String>,
},
Method::PATCH,
("https://www.test-domain.com/endpoint/{test_fragment}/test", test_fragment),
non_query_test_field: Option<bool>,
(
test_callback: Option<fn(usize)>
),
);
#[test]
fn test_new() {
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment");
assert_eq!(test_request.credentials, credentials);
assert_eq!(test_request.method, Method::PATCH);
assert_eq!(&test_request.url, "https://www.test-domain.com/endpoint/fragment/test");
assert_eq!(test_request.fields, None);
assert_eq!(test_request.test_field, None);
assert_eq!(test_request.kind, None);
assert_eq!(test_request.non_query_test_field, None);
assert_eq!(test_request.test_callback, None);
}
#[test]
fn test_fields() {
let fields = String::from("testField1, testField2");
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment")
.fields(&fields);
assert_eq!( test_request.fields, Some(fields) );
}
#[test]
fn test_parameters() {
let test_field = String::from("test");
let kind = String::from("testKind");
let expected_parameters = vec![
( "testField".to_string(), test_field.clone() ),
( "type".to_string(), kind.clone() ),
];
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment")
.test_field(&test_field)
.kind(&kind);
assert_eq!( test_request.test_field, Some(test_field) );
assert_eq!( test_request.kind, Some(kind) );
assert_eq!( test_request.get_parameters(), expected_parameters );
}
#[test]
fn test_non_query_fields() {
let non_query_test_field = true;
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment")
.non_query_test_field(non_query_test_field);
assert_eq!( test_request.non_query_test_field, Some(non_query_test_field) );
assert_eq!( test_request.get_parameters(), Vec::new() );
}
#[test]
fn test_callback() {
fn callback( arg: usize ) {
assert_eq!(arg, 0);
}
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment")
.test_callback(callback);
assert!( test_request.test_callback.is_some() );
(test_request.test_callback.unwrap())(0);
}
#[test]
fn test_build() {
let expected_url = Url::from_str(
"https://www.test-domain.com/endpoint/fragment/test?fields=testField1%2C+testField2&testField=test&type=testKind"
).unwrap();
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment")
.fields("testField1, testField2")
.test_field("test")
.kind("testKind")
.non_query_test_field(false);
let request = test_request.build().unwrap().build().unwrap();
assert_eq!( request.method(), &Method::PATCH );
assert_eq!( request.url(), &expected_url );
assert!( request.headers().get(header::AUTHORIZATION).unwrap().is_sensitive() );
}
#[test]
fn send_test() {
let credentials = INVALID_CREDENTIALS.clone();
let test_request = TestRequest::new(&credentials, "fragment");
let response = test_request.send();
if response.is_err() {
assert_eq!( response.unwrap_err().kind, ErrorKind::Response );
}
}
}