#![forbid(unsafe_code)]
#![warn(
absolute_paths_not_starting_with_crate,
anonymous_parameters,
box_pointers,
deprecated_in_future,
elided_lifetimes_in_paths,
explicit_outlives_requirements,
indirect_structural_match,
keyword_idents,
macro_use_extern_crate,
meta_variable_misuse,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
missing_doc_code_examples,
non_ascii_idents,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unstable_features,
unused_extern_crates,
unused_import_braces,
unused_lifetimes,
unused_qualifications,
unused_results,
variant_size_differences
)]
#![warn(
clippy::correctness,
clippy::restriction,
clippy::style,
clippy::pedantic,
clippy::complexity,
clippy::perf,
clippy::cargo,
clippy::nursery
)]
#![allow(
clippy::implicit_return,
clippy::missing_docs_in_private_items,
clippy::shadow_reuse,
clippy::similar_names,
clippy::else_if_without_else,
clippy::multiple_crate_versions,
clippy::module_name_repetitions,
clippy::print_stdout,
clippy::used_underscore_binding
)]
mod error;
pub mod requests;
pub mod types;
pub use error::Error;
use bytes::Bytes;
use http::{status::StatusCode, Request};
use std::{borrow::Cow, fmt, future::Future, time::Duration};
#[derive(Clone, Debug)]
pub struct Tenable<'a> {
pub auth: String,
pub uri: Cow<'a, str>,
}
impl Tenable<'_> {
#[must_use]
#[inline]
pub fn new(access_key: &str, secret_key: &str) -> Self {
Tenable {
auth: format!("accessKey={};secretKey={}", access_key, secret_key),
uri: Cow::Borrowed("https://cloud.tenable.com"),
}
}
#[inline]
#[allow(single_use_lifetimes)]
pub fn request<'a, O, R, CR, RE, F>(request: CR, fun: F) -> Result<O, Error<RE>>
where
CR: Into<Cow<'a, R>>,
R: 'a + HttpRequest<RE, Output = O>,
RE: fmt::Debug,
F: FnOnce(Request<Vec<u8>>) -> Result<Response, Error<RE>>,
{
let request = request.into();
let req = request.to_request()?;
let res = fun(req)?;
request.from_response(res)
}
#[inline]
#[allow(single_use_lifetimes)]
pub fn request_with_backoff<'a, O, R, CR, RE, F, BF>(
request: CR,
fun: F,
backoff_fun: BF,
) -> Result<O, Error<RE>>
where
CR: Into<Cow<'a, R>>,
R: 'a + HttpRequest<RE, Output = O>,
RE: fmt::Debug,
F: Fn(Request<Vec<u8>>) -> Result<Response, Error<RE>>,
BF: Fn(Duration) -> (),
{
let mut wait = Duration::from_millis(100);
let request = request.into();
loop {
let req = request.to_request()?;
let res = fun(req)?;
#[allow(clippy::wildcard_enum_match_arm)]
match request.from_response(res) {
Err(Error::RateLimitReached) => {
backoff_fun(wait);
match wait.checked_add(Duration::from_millis(100)) {
Some(new_wait) => wait = new_wait,
None => return Err(Error::MaximumWaitTimeReached),
}
}
other => return other,
}
}
}
#[inline]
#[allow(single_use_lifetimes, unused_lifetimes)]
pub async fn request_async<'a, O, R, CR, RE, F, Fut>(
request: CR,
fun: F,
) -> Result<O, Error<RE>>
where
CR: Into<Cow<'a, R>>,
R: 'a + HttpRequest<RE, Output = O>,
RE: fmt::Debug,
F: FnOnce(Request<Vec<u8>>) -> Fut,
Fut: Future<Output = Result<Response, Error<RE>>>,
{
let request = request.into();
let req = request.to_request()?;
let res = fun(req).await?;
request.from_response(res)
}
#[inline]
#[allow(single_use_lifetimes, unused_lifetimes)]
pub async fn request_with_backoff_async<'a, O, R, CR, RE, F, Fut, BF, FutBF>(
request: CR,
fun: F,
backoff_fun: BF,
) -> Result<O, Error<RE>>
where
CR: Into<Cow<'a, R>>,
R: 'a + HttpRequest<RE, Output = O>,
RE: fmt::Debug,
F: Fn(Request<Vec<u8>>) -> Fut,
Fut: Future<Output = Result<Response, Error<RE>>>,
BF: Fn(Duration) -> FutBF,
FutBF: Future<Output = ()>,
{
let mut wait = Duration::from_millis(100);
let request = request.into();
loop {
let req = request.to_request()?;
let res = fun(req).await?;
#[allow(clippy::wildcard_enum_match_arm)]
match request.from_response(res) {
Err(Error::RateLimitReached) => {
backoff_fun(wait).await;
match wait.checked_add(Duration::from_millis(100)) {
Some(new_wait) => wait = new_wait,
None => return Err(Error::MaximumWaitTimeReached),
}
}
other => return other,
}
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
pub status: StatusCode,
pub body: Bytes,
}
pub trait HttpRequest<RE: fmt::Debug>: Clone {
type Output;
fn to_request(&self) -> Result<Request<Vec<u8>>, Error<RE>>;
fn from_response(&self, res: Response) -> Result<Self::Output, Error<RE>>;
}