#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![warn(missing_docs)]
#![warn(noop_method_call)]
#![warn(unreachable_pub)]
#![warn(clippy::all)]
#![deny(clippy::await_holding_lock)]
#![deny(clippy::cargo_common_metadata)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::checked_conversions)]
#![warn(clippy::cognitive_complexity)]
#![deny(clippy::debug_assert_with_mut_call)]
#![deny(clippy::exhaustive_enums)]
#![deny(clippy::exhaustive_structs)]
#![deny(clippy::expl_impl_clone_on_copy)]
#![deny(clippy::fallible_impl_from)]
#![deny(clippy::implicit_clone)]
#![deny(clippy::large_stack_arrays)]
#![warn(clippy::manual_ok_or)]
#![deny(clippy::missing_docs_in_private_items)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::option_option)]
#![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)]
#![warn(clippy::rc_buffer)]
#![deny(clippy::ref_option_ref)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::trait_duplication_in_bounds)]
#![deny(clippy::unchecked_time_subtraction)]
#![deny(clippy::unnecessary_wraps)]
#![warn(clippy::unseparated_literal_suffix)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::mod_module_files)]
#![allow(clippy::let_unit_value)] #![allow(clippy::uninlined_format_args)]
#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![allow(clippy::collapsible_if)] #![deny(clippy::unused_async)]
use std::fmt;
use std::ops::{RangeInclusive, RangeToInclusive};
use std::path::Path;
use std::time::Duration;
pub mod error_sources;
pub mod intern;
pub mod iter;
pub mod n_key_list;
pub mod n_key_set;
pub mod rand_hostname;
pub mod rangebounds;
pub mod retry;
pub mod test_rng;
mod byte_qty;
pub use byte_qty::ByteQty;
pub use paste::paste;
use rand::Rng;
mod sealed {
pub trait Sealed {}
}
use sealed::Sealed;
pub fn skip_fmt<T>(_: &T, f: &mut fmt::Formatter) -> fmt::Result {
fn inner(f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "..")
}
inner(f)
}
pub fn iter_join(
separator: &str,
iter: impl Iterator<Item: fmt::Display> + Clone,
) -> impl fmt::Display {
struct Fmt<'a, I: Iterator<Item: fmt::Display> + Clone> {
separator: &'a str,
iter: I,
}
impl<'a, I: Iterator<Item: fmt::Display> + Clone> fmt::Display for Fmt<'a, I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { separator, iter } = self;
let mut iter = iter.clone();
if let Some(first) = iter.next() {
write!(f, "{first}")?;
}
for x in iter {
write!(f, "{separator}{x}")?;
}
Ok(())
}
}
Fmt { separator, iter }
}
pub trait StrExt: AsRef<str> {
fn strip_suffix_ignore_ascii_case(&self, suffix: &str) -> Option<&str> {
let whole = self.as_ref();
let suffix_start = whole.len().checked_sub(suffix.len())?;
whole[suffix_start..]
.eq_ignore_ascii_case(suffix)
.then(|| &whole[..suffix_start])
}
fn ends_with_ignore_ascii_case(&self, suffix: &str) -> bool {
self.strip_suffix_ignore_ascii_case(suffix).is_some()
}
}
impl StrExt for str {}
pub trait RngExt: Rng {
fn gen_range_checked<T, R>(&mut self, range: R) -> Option<T>
where
T: rand::distr::uniform::SampleUniform,
R: rand::distr::uniform::SampleRange<T>,
{
if range.is_empty() {
None
} else {
#[allow(clippy::disallowed_methods)]
Some(Rng::random_range(self, range))
}
}
fn gen_range_infallible<T>(&mut self, range: RangeToInclusive<T>) -> T
where
T: GenRangeInfallible,
{
self.gen_range_checked(T::lower_bound()..=range.end)
.expect("GenRangeInfallible type with an empty lower_bound()..=T range")
}
}
impl<T: Rng> RngExt for T {}
pub trait GenRangeInfallible: rand::distr::uniform::SampleUniform + Ord
where
RangeInclusive<Self>: rand::distr::uniform::SampleRange<Self>,
{
fn lower_bound() -> Self;
}
impl GenRangeInfallible for Duration {
fn lower_bound() -> Self {
Duration::ZERO
}
}
pub trait PathExt: Sealed {
fn display_lossy(&self) -> std::path::Display<'_>;
}
impl Sealed for Path {}
impl PathExt for Path {
#[allow(clippy::disallowed_methods)]
fn display_lossy(&self) -> std::path::Display<'_> {
self.display()
}
}
#[macro_export]
macro_rules! define_accessor_trait {
{
$( #[ $attr:meta ])*
$vis:vis trait $Trait:ident $( : $( $Super:path )* )? {
$( $accessor:ident: $type:ty, )*
$( + $( $rest:tt )* )?
}
} => {
$( #[ $attr ])*
$vis trait $Trait: $( core::convert::AsRef<$type> + )* $( $( $Super + )* )?
{
$(
fn $accessor(&self) -> &$type { core::convert::AsRef::as_ref(self) }
)*
$(
$( $rest )*
)?
}
}
}
#[macro_export]
macro_rules! macro_first_nonempty {
{ [ $($yes:tt)+ ] $($rhs:tt)* } => { $($yes)* };
{ [ ]$(,)? [ $($otherwise:tt)* ] $($rhs:tt)* } => {
$crate::macro_first_nonempty!{ [ $($otherwise)* ] $($rhs)* }
};
}
#[macro_export]
macro_rules! impl_debug_hex {
{ $type:ty $(,)? } => {
$crate::impl_debug_hex! { $type, |self_| <$type as AsRef<[u8]>>::as_ref(&self_) }
};
{ $type:ident . $($accessor:tt)+ } => {
$crate::impl_debug_hex! { $type, |self_| self_ . $($accessor)* .as_ref() }
};
{ $type:ty, $obtain:expr $(,)? } => {
impl std::fmt::Debug for $type {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::fmt::Write;
let obtain: fn(&$type) -> &[u8] = $obtain;
let bytes: &[u8] = obtain(self);
write!(f, "{}(", stringify!($type))?;
for b in bytes {
write!(f, "{:02x}", b)?;
}
write!(f, ")")?;
Ok(())
}
}
};
}
#[macro_export]
macro_rules! derive_serde_raw { {
$( #[ $($attrs:meta)* ] )*
$vis:vis struct $main:ident=$main_s:literal
$($body:tt)*
} => {
$(#[ $($attrs)* ])*
$vis struct $main
$($body)*
$crate::paste! {
#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize)]
#[serde(remote=$main_s)]
struct [< $main _Raw >]
$($body)*
}
} }
pub fn flatten<T, E>(x: Result<Result<T, E>, E>) -> Result<T, E> {
match x {
Ok(Ok(x)) => Ok(x),
Err(e) | Ok(Err(e)) => Err(e),
}
}
#[macro_export]
macro_rules! assert_val_impl_trait {
($check:expr, $trait:path $(,)?) => {{
fn ensure_trait<T: $trait>(_s: &T) {}
ensure_trait(&$check);
}};
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_time_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
use super::*;
#[test]
fn test_strip_suffix_ignore_ascii_case() {
assert_eq!(
"hi there".strip_suffix_ignore_ascii_case("THERE"),
Some("hi ")
);
assert_eq!("hi here".strip_suffix_ignore_ascii_case("THERE"), None);
assert_eq!("THERE".strip_suffix_ignore_ascii_case("there"), Some(""));
assert_eq!("hi".strip_suffix_ignore_ascii_case("THERE"), None);
}
}