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.

570 lines
13 KiB

use super::*;
use std::iter::FusedIterator;
use std::collections::BTreeSet;
use std::borrow::Borrow;
use futures::prelude::*;
use std::cell::RefCell;
use std::ptr;
/// General utilities
pub mod util;
/// Functions for manipulating byte slices
pub mod bytes;
// Internal modules
mod iters;
pub use iters::*;
mod streams;
pub use streams::*;
mod hashers;
pub use hashers::*;
mod hex;
pub use hex::*;
mod lag;
pub use lag::*;
mod defer_drop;
pub use defer_drop::*;
pub mod sync;
pub mod plex;
pub use plex::MultiplexStreamExt;
// The extension traits are defined in this `mod.rs` file, no need to re-export anything from here.
pub mod chunking;
/// How many elements should `precollect` allocate on the stack before spilling to the heap.
pub const PRECOLLECT_STACK_SIZE: usize = 64;
/// Implement `Default` for a type.
#[macro_export] macro_rules! default {
($ty:ty: $ex:expr) => {
impl Default for $ty
{
#[inline]
fn default() -> Self
{
$ex
}
}
}
}
/// Create a duration with time suffix `h`, `m`, `s`, `ms` or `ns`.
///
/// # Combination
/// These can also be combined.
/// ```
/// # use flan_utils::duration;
/// duration!(1 h, 20 m, 30 s);
/// ```
#[macro_export] macro_rules! duration
{
(0 $($_any:tt)?) => (::core::time::Duration::from_secs(0));
($dur:literal ms) => (::core::time::Duration::from_millis($dur));
($dur:literal ns) => (::core::time::Duration::from_nanos($dur));
($dur:literal s) => (::core::time::Duration::from_secs($dur));
($dur:literal m) => (::core::time::Duration::from_secs($dur * 60));
($dur:literal h) => (::core::time::Duration::from_secs($dur * 60 * 60));
( $($dur:literal $unit:tt),*)=> {
duration!(0 s) $(
+ duration!($dur $unit)
)*
};
}
/// Create a basic, C-like enum
#[macro_export] macro_rules! basic_enum {
($(#[$meta:meta])* $vis:vis $name:ident $(; $tcomment:literal)?: $($var:ident $(=> $comment:literal)?),+ $(,)?) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
$(#[doc = $tcomment])?
$vis enum $name {
$(
$(#[doc = $comment])?
$var
),+
}
}
}
/// Create a `Yes` or `No` enum.
#[macro_export] macro_rules! bool_type {
($vis:vis $name:ident $(; $comment:literal)? => $yes:ident, $no:ident) => {
basic_enum!(#[repr(u8)] $vis $name $(; $comment)?: $yes => "# First variant\n\nYes/true", $no => "# Second variant\n\nNo/false");
impl From<bool> for $name
{
#[inline] fn from(from: bool) -> Self
{
if from {
Self::$yes
} else {
Self::$no
}
}
}
impl From<$name> for bool
{
#[inline] fn from(from: $name) -> Self
{
match from {
$name::$yes => true,
$name::$no => false,
}
}
}
impl $name
{
/// Create from a bool value.
#[inline] pub const fn new(from: bool) -> Self
{
if from {
Self::$yes
} else {
Self::$no
}
}
/// Is this false?
#[inline] pub const fn is_no(self) -> bool
{
!self.is_yes()
}
/// Is this true?
#[inline] pub const fn is_yes(self) -> bool
{
match self {
Self::$yes => true,
Self::$no => false,
}
}
/// Return Some(T) if self is true.
#[inline] pub fn some<T>(self, value: T) -> Option<T>
{
self.and_then(move || value)
}
/// Map this value
#[inline] pub fn map<F, T>(self, f: F) -> T
where F: FnOnce(bool) -> T
{
f(self.is_yes())
}
/// Run this closure if value is false
#[inline] pub fn or_else<F, T>(self, f: F) -> Option<T>
where F: FnOnce() -> T
{
if let Self::$no = self {
Some(f())
} else {
None
}
}
/// Run this closure if value is true
#[inline] pub fn and_then<F, T>(self, f: F) -> Option<T>
where F: FnOnce() -> T
{
if let Self::$yes = self {
Some(f())
} else {
None
}
}
/// Return `yes` if true and `no` if false
#[inline] pub fn either<T>(self, yes: T, no: T) -> T
{
self.and_either(move || yes, move || no)
}
/// Run closure `yes` if value is true, `no` if value is false.
#[inline] pub fn and_either<F, G, T>(self, yes: F, no: G) -> T
where F: FnOnce() -> T,
G: FnOnce() -> T,
{
match self {
Self::$yes => yes(),
Self::$no => no(),
}
}
}
};
($vis:vis $name:ident $(; $comment:literal)?) => {
$crate::bool_type!($vis $name $(; $comment)? => Yes, No);
}
}
/// Create an accessor method. for a field in a structure.
///
/// The supported accessor types are: `ref`, `mut`, and `move`.
#[macro_export] macro_rules! accessor {
($vis:vis ref $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> &$ty {
&self.$internal
}
};
($vis:vis ref $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> &$ty {
&self.$internal
}
};
($vis:vis mut $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> &mut $ty {
&mut self.$internal
}
};
($vis:vis mut $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> &mut $ty {
&mut self.$internal
}
};
($vis:vis move $name:ident -> $ty:ty => $internal:ident $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> $ty {
self.$internal
}
};
($vis:vis move $name:ident -> $ty:ty => $internal:tt $(; $comment:literal)?) => {
$(#[doc=$comment])?
#[inline] $vis fn $name(&self) -> $ty {
self.$internal
}
};
}
/// Collect an iterator's output and then drop it to detach the iterator from any references or resources it might have.
#[macro_export] macro_rules! precollect {
($iter:expr, $num:literal) => {
{
{
let it: ::smallvec::SmallVec<[_; $num]> = $iter.into_iter().collect();
it.into_iter()
}
}
};
($iter:expr) => {
{
{
let it: ::smallvec::SmallVec<[_; $crate::ext::PRECOLLECT_STACK_SIZE]> = $iter.into_iter().collect();
it.into_iter()
}
}
}
}
#[macro_export] macro_rules! no_op {
($expr:expr) => {
{
$expr;
}
};
}
/// 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");
/// Execute this expression if running in debug mode.
#[macro_export] macro_rules! debug_expr {
(if $expr:expr; else $or:expr) => {
if cfg!(debug_assertions) { $expr }
else { $or }
};
(else $expr:expr) => {
if !cfg!(debug_assertions) { $expr }
};
($expr:expr) => {
if cfg!(debug_assertions) { $expr }
};
}
/// Run each expression in sequence then return the result of the first one.
///
/// # Example
/// ```
/// # use mtfse::prog1;
/// assert_eq!(prog1 {
/// 1 + 2;
/// println!("Done thing");
/// 4 + 5;
/// }, 3);
/// ```
#[macro_export] macro_rules! prog1 {
($first:expr; $($rest:expr);* $(;)?) => {
($first, $( $rest ),*).0
}
}
/// Helper to create an `std::io::Error` structure.
///
/// # Example
/// ```
/// # use mtfse::io_err;
/// let err = io_err!(Other, "some error");
/// ```
#[macro_export] macro_rules! io_err {
($kind:ident, $msg:expr) => {
::std::io::Error::new(::std::io::ErrorKind::$kind, $msg)
};
($msg:expr) => (io_err!(Other, $msg));
}
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,
Err(_) => unsafe { debug_unreachable!() },
}
}
}
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,
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,
Ok(_) => unsafe { debug_unreachable!() },
}
}
}
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,
Ok(_) => unsafe { debug_unreachable!() },
}
}
}
pub trait ChunkStreamExt<T>: Sized
{
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> chunking::ChunkingStream<Self,T,I>;
fn chunk(self, sz: usize) -> chunking::ChunkingStream<Self, T>
{
self.chunk_into(sz)
}
}
impl<S, T> ChunkStreamExt<T> for S
where S: Stream<Item=T>
{
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> chunking::ChunkingStream<Self,T,I>
{
chunking::ChunkingStream::new(self, sz)
}
}
pub trait ChunkIterExt<T>: Sized
{
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> iters::ChunkingIter<Self,T,I>;
fn chunk(self, sz: usize) -> iters::ChunkingIter<Self, T>
{
self.chunk_into(sz)
}
}
impl<S, T> ChunkIterExt<T> for S
where S: Iterator<Item=T>
{
fn chunk_into<I: From<Vec<T>>>(self, sz: usize) -> iters::ChunkingIter<Self,T,I>
{
iters::ChunkingIter::new(self, sz)
}
}