You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
8.3 KiB
353 lines
8.3 KiB
//! Extensions
|
|
use super::*;
|
|
use std::{
|
|
borrow::{
|
|
Borrow, ToOwned,
|
|
},
|
|
iter,
|
|
};
|
|
|
|
|
|
pub trait Tuple2MapExt<T>
|
|
{
|
|
fn map<F, U>(self, fun: F) -> (U, U)
|
|
where F: FnMut(T) -> U;
|
|
}
|
|
|
|
impl<T> Tuple2MapExt<T> for (T,T)
|
|
{
|
|
fn map<F, U>(self, mut fun: F) -> (U, U)
|
|
where F: FnMut(T) -> U
|
|
{
|
|
(fun(self.0), fun(self.1))
|
|
}
|
|
}
|
|
|
|
pub trait JitterExt<T>
|
|
{
|
|
/// Produce a random value between `self.0` and `self.1` inclusive
|
|
fn jitter(self) -> T;
|
|
}
|
|
|
|
impl<T> JitterExt<T> for (T, T)
|
|
where T: rand::distributions::uniform::SampleUniform
|
|
{
|
|
fn jitter(self) -> T
|
|
{
|
|
util::jitter(self.0, self.1)
|
|
}
|
|
}
|
|
|
|
pub trait Unreference<T>
|
|
{
|
|
fn cloned(self) -> Option<T>;
|
|
}
|
|
|
|
impl<'a, T> Unreference<T> for Option<&'a T>
|
|
where T: Clone
|
|
{
|
|
fn cloned(self) -> Option<T> {
|
|
self.map(Clone::clone)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// An iterator over `char` that maps certain characters to others
|
|
pub struct CharSubstituteIter<'map, I, T= char>
|
|
where I: Iterator<Item = T>,
|
|
{
|
|
iter: I,
|
|
map: &'map smallmap::Map<char, char>,
|
|
}
|
|
|
|
impl<'a, I, T> Iterator for CharSubstituteIter<'a, I, T>
|
|
where I: Iterator<Item = T>,
|
|
T: From<char> + smallmap::Collapse,
|
|
char: Borrow<T>
|
|
{
|
|
type Item = T;
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
self.iter.next()
|
|
.map(|item| self.map.get(&item)
|
|
.cloned()
|
|
.map(T::from)
|
|
.unwrap_or(item))
|
|
}
|
|
|
|
#[inline] fn size_hint(&self) -> (usize, Option<usize>) {
|
|
self.iter.size_hint()
|
|
}
|
|
}
|
|
|
|
|
|
impl<'a, I, T> DoubleEndedIterator for CharSubstituteIter<'a, I, T>
|
|
where I: Iterator<Item = T> + DoubleEndedIterator,
|
|
T: From<char> + smallmap::Collapse,
|
|
char: Borrow<T>
|
|
{
|
|
fn next_back(&mut self) -> Option<Self::Item>
|
|
{
|
|
self.iter.next_back()
|
|
.map(|item| self.map.get(&item)
|
|
.cloned()
|
|
.map(T::from)
|
|
.unwrap_or(item))
|
|
}
|
|
}
|
|
|
|
impl<'a, I, T> iter::FusedIterator for CharSubstituteIter<'a, I, T>
|
|
where I: Iterator<Item = T> + iter::FusedIterator,
|
|
T: From<char> + smallmap::Collapse,
|
|
char: Borrow<T>{}
|
|
|
|
impl<'a, I, T> iter::ExactSizeIterator for CharSubstituteIter<'a, I, T>
|
|
where I: Iterator<Item = T> + ExactSizeIterator,
|
|
T: From<char> + smallmap::Collapse,
|
|
char: Borrow<T>{}
|
|
|
|
pub trait CharMapExt<T>: Sized + IntoIterator<Item=T>
|
|
{
|
|
/// Creates an iterator that maps chars over this one
|
|
fn replace_chars(self, map: &smallmap::Map<char, char>) -> CharSubstituteIter<'_, Self::IntoIter, T>;
|
|
}
|
|
|
|
impl<S, T> CharMapExt<T> for S
|
|
where S: IntoIterator<Item=T>,
|
|
T: From<char> + smallmap::Collapse,
|
|
char: Borrow<T>
|
|
{
|
|
#[inline] fn replace_chars(self, map: &smallmap::Map<char, char>) -> CharSubstituteIter<'_, Self::IntoIter, T> {
|
|
CharSubstituteIter {
|
|
iter: self.into_iter(),
|
|
map,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The ID type used for backing ID types;
|
|
pub type GenericID = uuid::Uuid;
|
|
|
|
|
|
/// Create a type that contains a (globally) unique ID.
|
|
#[macro_export] macro_rules! id_type {
|
|
($name:ident $(: $doc:literal)?) => ($crate::id_type!{pub(self) $name $(: $doc)?});
|
|
($vis:vis $name:ident $(: $doc:literal)?) => {
|
|
$(#[doc=$doc])?
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
|
$vis struct $name($crate::ext::GenericID);
|
|
|
|
impl $name
|
|
{
|
|
/// Create a new unique ID.
|
|
#[inline(always)] fn id_new() -> Self
|
|
{
|
|
Self($crate::ext::GenericID::new_v4())
|
|
}
|
|
|
|
/// The generic ID type backing this one
|
|
#[inline(always)] fn id_generic(&self) -> &$crate::ext::GenericID
|
|
{
|
|
&self.0
|
|
}
|
|
|
|
/// Consume into the generic ID
|
|
#[inline(always)] fn id_into_generic(self) -> $crate::ext::GenericID
|
|
{
|
|
self.0
|
|
}
|
|
|
|
/// Create from a generic ID
|
|
#[inline(always)] fn id_from_generic(gen: $crate::ext::GenericID) -> Self
|
|
{
|
|
Self(gen)
|
|
}
|
|
}
|
|
|
|
impl ::std::fmt::Display for $name
|
|
{
|
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result
|
|
{
|
|
use ::std::fmt::Write;
|
|
f.write_str(concat!(stringify!($name),"<"))?;
|
|
self.0.fmt(f)?;
|
|
f.write_str(">")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Expands to `unreachable_unchecked` in non-debug builds.
|
|
///
|
|
/// # Safety
|
|
/// You must make 100% sure this code path will never be entered, or it will cause undefined behaviour in release builds.
|
|
#[macro_export] macro_rules! debug_unreachable {
|
|
() => {
|
|
if cfg!(debug_assertions) {
|
|
#[cold] unreachable!()
|
|
} else {
|
|
::std::hint::unreachable_unchecked()
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
/// Dirty debugging macro to get the compiler to print an error message telling you the size of a type.
|
|
/// ```
|
|
/// check_size!((u8, u8)); // Expected ... found one with *2* elements
|
|
/// ```
|
|
/// # Size assertions
|
|
/// Can also be used to statically assert the size of a type
|
|
/// ```
|
|
/// # use datse::ext::check_size;
|
|
/// check_size!(u16 where == 2; "u16 should be 2 bytes");
|
|
/// check_size!(u16 where < 3; "u16 should be lower than 3 bytes");
|
|
/// check_size!(u16 where > 1; "u16 should be larger that 1 byte");
|
|
/// check_size!(u16 where != 0; "u16 should be larger that 0 bytes");
|
|
/// ```
|
|
///
|
|
/// You can also combine multiple
|
|
/// ```
|
|
/// # use datse::ext::check_size;
|
|
/// check_size!([u8; 512] where {
|
|
/// != 10;
|
|
/// > 511;
|
|
/// < 513;
|
|
/// == 512
|
|
/// });
|
|
/// ```
|
|
///
|
|
/// This can be used to give you prompts as to when you might want to consider boxing a type.
|
|
#[macro_export] macro_rules! check_size {
|
|
($t:ty) => {
|
|
const _: [(); 0] = [(); ::std::mem::size_of::<$t>()];
|
|
};
|
|
|
|
($t:ty where == $n:literal $(; $msg:literal)?) => {
|
|
const _: [(); $n] = [(); ::std::mem::size_of::<$t>()];
|
|
};
|
|
|
|
($t:ty where {$($op:tt $n:literal);+} $(; $msg:literal)?) => {
|
|
$(
|
|
$crate::static_assert!(::std::mem::size_of::<$t>() $op $n);
|
|
)+
|
|
};
|
|
|
|
($t:ty where $op:tt $n:literal $(; $msg:literal)?) => {
|
|
$crate::static_assert!(::std::mem::size_of::<$t>() $op $n $(; $msg)?);
|
|
};
|
|
}
|
|
|
|
check_size!(u8 where == 1);
|
|
check_size!(u16 where > 1);
|
|
check_size!([u8; 512] where <= 512);
|
|
check_size!([u8; 512] where {
|
|
!= 10;
|
|
> 511;
|
|
< 513;
|
|
== 512
|
|
});
|
|
|
|
|
|
/// Assert the output of a constant boolean expression is `true` at compile time.
|
|
#[macro_export] macro_rules! static_assert {
|
|
($val:expr $(; $msg:literal)?) => {
|
|
const _: [(); 1] = [(); ($val as bool) as usize];
|
|
}
|
|
}
|
|
|
|
/// Assert a trait is object safe. This will produce a compiler error if the trait is not object safe
|
|
#[macro_export] macro_rules! assert_object_safe {
|
|
($trait:path $(; $msg:literal)?) => {
|
|
const _:() = {
|
|
#[cold] fn __assert_object_safe() -> !
|
|
{
|
|
let _: &dyn $trait;
|
|
unsafe {
|
|
debug_unreachable!()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
assert_object_safe!(AsRef<str>; "object safety assertion test");
|
|
static_assert!(1+1==2; "static assertion test");
|
|
|
|
|
|
pub trait UnwrapInfallible<T>
|
|
{
|
|
fn unwrap_infallible(self) -> T;
|
|
}
|
|
|
|
impl<T> UnwrapInfallible<T> for Result<T, std::convert::Infallible>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Err`.
|
|
#[inline(always)] fn unwrap_infallible(self) -> T {
|
|
match self {
|
|
Ok(v) => v,
|
|
#[cold] Err(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(nightly)]
|
|
impl<T> UnwrapInfallible<T> for Result<T, !>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Err`.
|
|
#[inline(always)] fn unwrap_infallible(self) -> T {
|
|
match self {
|
|
Ok(v) => v,
|
|
#[cold] Err(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
pub trait UnwrapErrInfallible<T>
|
|
{
|
|
fn unwrap_err_infallible(self) -> T;
|
|
}
|
|
|
|
impl<T> UnwrapErrInfallible<T> for Result<std::convert::Infallible, T>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Ok`.
|
|
#[inline(always)] fn unwrap_err_infallible(self) -> T {
|
|
match self {
|
|
Err(v) => v,
|
|
#[cold] Ok(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#[cfg(nightly)]
|
|
impl<T> UnwrapErrInfallible<T> for Result<!, T>
|
|
{
|
|
/// Unwrap with 0 overhead for values that cannot possibly be `Ok`.
|
|
#[inline(always)] fn unwrap_err_infallible(self) -> T {
|
|
match self {
|
|
Err(v) => v,
|
|
#[cold] Ok(_) => unsafe { debug_unreachable!() },
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Declair a `submodule`.
|
|
///
|
|
/// Load the private module, and then re-export all its internals.
|
|
#[macro_export] macro_rules! submod {
|
|
(priv $name:ident $(; $doc:literal)?) => {
|
|
$(#[doc=$doc])?
|
|
mod $name;
|
|
use self::$name::*;
|
|
};
|
|
($vis:vis $name:ident $(; $doc:literal)?) => {
|
|
$(#[doc=$doc])?
|
|
mod $name;
|
|
$vis use self::$name::*;
|
|
};
|
|
($name:ident $(; $doc:literal)?) => ($crate::submod!(pub $name $(; $doc)?));
|
|
}
|