Skip to content

Commit

Permalink
Version 2.0.0. (#13)
Browse files Browse the repository at this point in the history
* Blocking API (#9)

* initial implementation of a blocking api

* fixed clippy

* fixed some things:

- clippy
- made the feature actually public
- added tests
- updated github ci

* implemented blocking api with the blocking feature from reqwest

- removed tokio as a dependency

* made clippy happy + !!!breaking changes!!!

* fixed github ci

* updated documentations

* renamed `as_number` to `asn` (#12)

* moved tests into separate directory + added some more (#14)

* updated version to `2.0.0`
  • Loading branch information
DenuxPlays authored Nov 27, 2023
1 parent 4892828 commit 0c4fcbe
Show file tree
Hide file tree
Showing 22 changed files with 607 additions and 188 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = tab
indent_style = space
indent_size = 4
max_line_length = 120

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --release --verbose
run: cargo build --all-targets --all-features --release --verbose
- name: Run tests
run: cargo test --release --verbose
run: cargo test --all-targets --all-features --release --verbose
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ip-api4rs"
version = "1.0.0"
version = "2.0.0"
authors = ["Timon Klinkert <[email protected]>"]
description = "A Rust library for the ip-api.com API."
license = "Apache-2.0"
Expand All @@ -11,14 +11,19 @@ readme = "README.md"
edition = "2021"
rust-version = "1.73.0"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
blocking = ["reqwest/blocking"]

[dev-dependencies]
# Async runtime
tokio = { version = "1.33.0", features = ["rt", "macros", "rt-multi-thread"] }

[dependencies]
#Async traits
async-trait = "0.1.74"

# HTTP client
reqwest = { version = "0.11", features = ["json"] }
reqwest = { version = "0.11", features = ["json", "blocking"] }

# Ratelimiting
governor = "0.6.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async fn main() {

## Features

- fully async api
- fully async api (or blocking with the `blocking` feature)
- simple to use
- supply custom structs to only get want you want
- Api-Token support
Expand Down
1 change: 1 addition & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ edition = "2021"
max_width = 120
newline_style = "Unix"
use_small_heuristics = "Off"
hard_tabs = false
79 changes: 79 additions & 0 deletions src/blocking/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
use nonzero_ext::nonzero;
use reqwest::blocking::Client;
use serde::de::DeserializeOwned;

use crate::client::{BlockingIpApi, IpApi};
use crate::error::IpApiError;
use crate::model::ip_response::{IpDefaultResponse, IpFullResponse};
use crate::{request_handler, util};

/// A client for the ip-api.com API that blocks the current thread.
pub struct BlockingIpApiClient {
/// The client to use for the requests.
pub client: Client,
/// The rate limiter to use for the requests.
pub limiter: Option<DefaultDirectRateLimiter>,
/// The API key to use for the requests.
pub api_key: Option<String>,
}

impl Default for BlockingIpApiClient {
fn default() -> Self {
Self::new()
}
}

impl BlockingIpApiClient {
/// Creates a new BlockingIpApiClient with no API key.
pub fn new() -> Self {
Self {
client: Client::new(),
limiter: Some(RateLimiter::direct(Quota::per_minute(nonzero!(45u32)))),
api_key: None,
}
}

/// Creates a new BlockingIpApiClient with an API key.
pub fn new_with_api_key(api_key: String) -> Self {
Self {
client: Client::new(),
limiter: None,
api_key: Some(api_key),
}
}
}

impl IpApi for BlockingIpApiClient {
fn get_api_key(&self) -> &Option<String> {
&self.api_key
}

fn get_rate_limiter(&self) -> &Option<DefaultDirectRateLimiter> {
&self.limiter
}
}

impl BlockingIpApi for BlockingIpApiClient {
fn query_api_default(&self, ip: &str) -> Result<IpDefaultResponse, IpApiError> {
let request = util::requests::get_default_blocking_get_request(&ip.to_string(), self);
request_handler::perform_blocking_get_request::<IpDefaultResponse>(request, &self.limiter)
}

fn query_api_fully(&self, ip: &str) -> Result<IpFullResponse, IpApiError> {
let request = util::requests::get_blocking_get_request::<IpFullResponse>(&ip.to_string(), self);
request_handler::perform_blocking_get_request::<IpFullResponse>(request, &self.limiter)
}

fn query_api<T>(&self, ip: &str) -> Result<T, IpApiError>
where
T: DeserializeOwned,
{
let request = util::requests::get_blocking_get_request::<T>(&ip.to_string(), self);
request_handler::perform_blocking_get_request::<T>(request, &self.limiter)
}

fn get_http_client(&self) -> &Client {
&self.client
}
}
1 change: 1 addition & 0 deletions src/blocking/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod client;
101 changes: 101 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use async_trait::async_trait;
use governor::DefaultDirectRateLimiter;
use reqwest::blocking;
use serde::de::DeserializeOwned;

use crate::error::IpApiError;
use crate::model::ip_response::{IpDefaultResponse, IpFullResponse};

/// The main client for the ip-api.com API.
pub trait IpApi {
/// Gets the optional API key.
///
/// # Returns
/// * `Option<String>` - The optional API key.
fn get_api_key(&self) -> &Option<String>;

/// Gets the rate limiter.
///
/// # Returns
/// * `&DefaultDirectRateLimiter` - The rate limiter.
fn get_rate_limiter(&self) -> &Option<DefaultDirectRateLimiter>;
}

/// The blocking client for the ip-api.com API.
#[cfg(feature = "blocking")]
pub trait BlockingIpApi: IpApi {
/// Queries the API with the default fields.
///
/// # Arguments
/// * `ip` - The IP address to query.
///
/// # Returns
/// * `IpDefaultResponse` - The response from the API.
fn query_api_default(&self, ip: &str) -> Result<IpDefaultResponse, IpApiError>;

/// Queries the API with all fields.
///
/// # Arguments
/// * `ip` - The IP address to query.
///
/// # Returns
/// * `IpFullResponse` - The response from the API.
fn query_api_fully(&self, ip: &str) -> Result<IpFullResponse, IpApiError>;

/// Queries the API with a custom struct.
///
/// # Arguments
/// * `ip` - The IP address to query.
/// * `T` - The custom struct to deserialize the response into.
///
/// # Returns
/// * `T` - The response from the API.
fn query_api<T>(&self, ip: &str) -> Result<T, IpApiError>
where
T: DeserializeOwned;

/// Gets you the blocking http client.
///
/// # Returns
/// * `&reqwest::blocking::Client` - The blocking http client.
fn get_http_client(&self) -> &blocking::Client;
}

/// The async client for the ip-api.com API.
#[async_trait]
pub trait AsyncIpApi: IpApi {
/// Queries the API with the default fields.
///
/// # Arguments
/// * `ip` - The IP address to query.
///
/// # Returns
/// * `IpDefaultResponse` - The response from the API.
async fn query_api_default(&self, ip: &str) -> Result<IpDefaultResponse, IpApiError>;

/// Queries the API with all fields.
///
/// # Arguments
/// * `ip` - The IP address to query.
///
/// # Returns
/// * `IpFullResponse` - The response from the API.
async fn query_api_fully(&self, ip: &str) -> Result<IpFullResponse, IpApiError>;

/// Queries the API with a custom struct.
///
/// # Arguments
/// * `ip` - The IP address to query.
/// * `T` - The custom struct to deserialize the response into.
///
/// # Returns
/// * `T` - The response from the API.
async fn query_api<T>(&self, ip: &str) -> Result<T, IpApiError>
where
T: DeserializeOwned;
/// Gets you the async http client.
///
/// # Returns
/// * `&reqwest::Client` - The async http client.
fn get_http_client(&self) -> &reqwest::Client;
}
Loading

0 comments on commit 0c4fcbe

Please sign in to comment.