1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
//! # api-request-utils-rs
//! [](https://crates.io/crates/api-request-utils)
//! [](https://docs.rs/api-request-utils)
//!
//! This library aims to provide a straightforward and efficient solution for making api requests It is designed to be user-friendly, customizable, and extensible, allowing developers to easily integrate and interact with APIs in their Rust applications.
//!
//! ## Features
//!
//! - Convenient functions for sending HTTP requests and handling responses.
//! - Error handling utilities for handling different types of request errors.
//! - JSON serialization and deserialization helpers.
//! - Parameter encoding and query string generation utilities.
//! - Request builder and modifier traits for customization and extensibility.
//!
//! ## Installation
//!
//! Add the following line to your `Cargo.toml` file:
//!
//! ```toml
//! api-request-utils = "0.1.6"
//! ```
//!
//! To enable the export feature and include the specified dependencies `(reqwest,serde_json, serde(with derive))`
//!
//! ```toml
//! api-request-utils = { version = "0.1.6", features = ["export"]}
//! ```
//!
//! ## Usage
//!
//! Import the required modules and types in your Rust code:
//! ```rust
//! use api_request_utils::{
//! ParameterHashMap,
//! RequestError,
//! RequestHandler,
//! RequestInfo
//! };
//! ```
//!
//! Then implement the `RequestInfo` trait for your API client struct. Trait to provide some basic info about API :
//!
//! ```rust
//! struct MyApiClient;
//!
//! impl RequestInfo for MyApiClient {
//! ...
//! }
//! ```
//!
//! Then implement the `RequestModifiers` trait for your API client struct. This trait provides methods for modifying the struct in a specific way:
//!
//! ```rust
//! impl RequestModifiers for MyApiClient {
//! ...
//! }
//! ```
//!
//! Then implement the `RequestHandler` trait for your API client struct. This trait provides the request method for sending HTTP requests :
//!
//! ```rust
//! impl RequestHandler for MyApiClient {
//! ...
//! }
//! ```
//!
//! Now just combine the methods , data and parameters / json to make the request and handle the error
//!
//! Please note that the examples provided here are simplified and serve as a starting point. For comprehensive documentation of the crate, please visit the [crate documentation](https://docs.rs/api-request-utils-rs) for a better understanding of the crate's functionalities and APIs.
//!
//! ## Contributing
//! Contributions are welcome! If you find any issues or have suggestions for improvement, please open an issue or submit a pull request.
use std::collections::HashMap;
#[cfg(feature = "export")]
pub use reqwest;
#[cfg(feature = "export")]
pub use serde_json;
#[cfg(feature = "export")]
pub use serde;
/// A HashMap type used for storing parameters with optional values.
///
/// The keys are string references, and the values are optional string references.
pub type ParameterHashMap<'a> = HashMap<&'a str, Option<&'a str>>;
/// Enum representing different types of request errors.
///
/// The `Internal` variant represents internal errors with an associated string message.
/// The `Json` variant represents errors related to JSON deserialization with an associated error value.
pub enum RequestError<E> {
/// Internal error variant with an associated string message.
Internal(String),
/// JSON error variant with an associated error value.
Json(E),
}
/// A trait for handling HTTP requests.
#[async_trait::async_trait]
pub trait RequestHandler {
/// Sends a request using the given RequestBuilder and handles the response.
///
/// # Examples
///
/// ```rust
/// #[tokio::main]
/// async fn main() {
/// let url = "https://api.example.com";
/// let request = reqwest::Client::new().get(url);
///
/// match request::<serde_json::Value, serde_json::Value>(request).await {
/// Ok(response) => {
/// println!("Response: {:?}", response);
/// }
/// Err(error) => {
/// match error {
/// RequestError::Internal(message) => {
/// eprintln!("Internal Error: {}", message);
/// }
/// RequestError::Json(error_data) => {
/// eprintln!("JSON Error: {:?}", error_data);
/// }
/// }
/// }
/// }
/// }
/// ```
async fn request<T,E>(request: reqwest::RequestBuilder) -> Result<T,RequestError<E>> where T : serde::de::DeserializeOwned , E : serde::de::DeserializeOwned {
let response_result = request.send().await;
match response_result {
Err(error) => return Err(RequestError::Internal(error.to_string())),
Ok(response) => {
let status = response.status();
let body_result = response.text().await;
if body_result.is_err() {
return Err(RequestError::Internal("Error in reading response body".to_string()));
};
let body_string = body_result.unwrap();
match status.is_success() {
true => return Ok(serde_json::from_str(&body_string).unwrap()),
false => return Err(RequestError::Json(serde_json::from_str(&body_string).unwrap())),
}
}
};
}
}
/// This trait provides methods for modifying the struct in a specific way:
pub trait RequestModifiers : RequestInfo {
/// Adds an Authorization header to the given RequestBuilder with the provided token.
///
/// The Authorization header follows the format "Bearer TOKEN", where TOKEN is the
/// authentication token used for authorization.
///
/// # Arguments
///
/// * request_builder - The RequestBuilder to add the header to.
/// * token - The authentication token to include in the Authorization header.
///
/// # Returns
///
/// The modified RequestBuilder with the Authorization header added.
///
/// # Example
///
/// ```rust
/// use reqwest::RequestBuilder;
/// let request_builder = reqwest::Client::new().get("https://example.com");
/// let token = "YOUR_AUTH_TOKEN";
/// let modified_request_builder = authorization_header(&request_builder, token);
/// ```
fn authorization_header(request_builder : reqwest::RequestBuilder,token : &str) -> reqwest::RequestBuilder {
request_builder.header("Authorization",format!(" Bearer {}",token))
}
/// Joins the given endpoint with the base URL.
///
/// # Arguments
///
/// * `endpoint` - The endpoint to join with the base URL.
///
/// # Returns
///
/// The joined URL as a `String`.
///
/// # Example
///
/// ```rust
/// struct MyStruct;
/// impl RequestHandler for ... {
/// const BASE_URL: &'static str = "https://api.example.com";
/// }
/// fn main(){
/// let url = MyStruct::create_endpoint("get");
/// assert_eq!(url,"https://api.example.com/get"); // using the default implementation
/// }
/// ```
fn create_endpoint(endpoint : &str) -> String {
format!("{}/{}",Self::BASE_URL,endpoint)
}
}
pub trait RequestDefaults : RequestModifiers {
/// Returns the reqwest::Client instance associated with the API client.
///
/// The client is used to send HTTP requests to the API.
///
/// # Examples
///
/// ```rust
/// fn main() {
/// let api_client = APIClient::new();
/// let client = api_client.client();
///
/// // Use the client to make HTTP requests
/// // ...
/// }
fn client(&self) -> reqwest::Client;
/// Modifies the provided `RequestBuilder` with default headers.
///
/// # Arguments
///
/// * `request_builder` - The `RequestBuilder` to modify.
///
/// # Returns
///
/// The modified `RequestBuilder` with default headers set.
fn default_headers(request_builder : reqwest::RequestBuilder) -> reqwest::RequestBuilder {
request_builder
}
/// Modifies the provided `RequestBuilder` with default parameters.
///
/// # Arguments
///
/// * `request_builder` - The `RequestBuilder` to modify.
///
/// # Returns
///
/// The modified `RequestBuilder` with default parameters set.
fn default_parameters(request_builder : reqwest::RequestBuilder) -> reqwest::RequestBuilder {
request_builder
}
/// Modifies the provided `RequestBuilder` with default settings for post request.
///
/// # Arguments
///
/// * `endpoint` - The endpoint for the request.
/// * `json` - The JSON payload for the request.
///
/// # Returns
///
/// The modified `RequestBuilder` with default settings applied.
fn default_post_requestor(&self,endpoint : &str, json : &str) -> reqwest::RequestBuilder {
self.client().post(Self::create_endpoint(endpoint)).body(json.to_owned())
}
/// Modifies the provided `RequestBuilder` with default settings for get request.
///
/// # Arguments
///
/// * `endpoint` - The endpoint for the request.
/// * `parameters` - The Parameters for the request.
///
/// # Returns
///
/// The modified `RequestBuilder` with default settings applied.
fn default_get_requestor<'a>(&self,endpoint : &str,parameters : ParameterHashMap<'a>) -> reqwest::RequestBuilder {
self.client().get(Self::create_endpoint(endpoint)).query(¶meters)
}
}
// Trait to provide some basic info about API
pub trait RequestInfo {
/// The base URL for the requests.
const BASE_URL : &'static str;
/// The API key as string used for authentication.
const API_KEY_STR : Option<&'static str> = Some("apiKey");
}