|
|
|
@ -17,6 +17,52 @@ use tokio::{
|
|
|
|
|
};
|
|
|
|
|
use futures::future::Future;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct GroupIter<I, T>(std::iter::Fuse<I>, Vec<T>, usize);
|
|
|
|
|
|
|
|
|
|
impl<I: Iterator<Item=T>, T> Iterator for GroupIter<I, T>
|
|
|
|
|
{
|
|
|
|
|
type Item = Box<[T]>;
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
|
|
|
{
|
|
|
|
|
if self.1.len() == 0 {
|
|
|
|
|
// fill
|
|
|
|
|
for (_, item) in (0..self.2).zip(&mut self.0)
|
|
|
|
|
{
|
|
|
|
|
self.1.push(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if self.1.len() == 0 {
|
|
|
|
|
None
|
|
|
|
|
} else{
|
|
|
|
|
Some(std::mem::replace(&mut self.1, Vec::with_capacity(self.2)).into())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
let (low, high) = self.0.size_hint();
|
|
|
|
|
(low / self.2, high.map(|x| (x / self.2) + 1)) //not too sure if this is right...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<I: Iterator<Item=T>, T> std::iter::FusedIterator for GroupIter<I, T>{}
|
|
|
|
|
|
|
|
|
|
pub trait GroupIterExt<I, T>: Sized
|
|
|
|
|
{
|
|
|
|
|
/// Group this iterator to return a boxed slice of every `n` items.
|
|
|
|
|
///
|
|
|
|
|
/// # Notes
|
|
|
|
|
/// If there isn't `n` items left in the iterator, then the rest is returned.
|
|
|
|
|
fn group(self, n: usize) -> GroupIter<I, T>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: IntoIterator> GroupIterExt<<T as IntoIterator>::IntoIter, <T as IntoIterator>::Item> for T
|
|
|
|
|
{
|
|
|
|
|
fn group(self, every: usize) -> GroupIter<<T as IntoIterator>::IntoIter, <T as IntoIterator>::Item>
|
|
|
|
|
{
|
|
|
|
|
GroupIter(self.into_iter().fuse(), Vec::with_capacity(every), every)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait JoinStrsExt: Sized
|
|
|
|
|
{
|
|
|
|
|
/// Join an iterator of `str` with a seperator
|
|
|
|
@ -24,14 +70,15 @@ pub trait JoinStrsExt: Sized
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T,I> JoinStrsExt for I
|
|
|
|
|
where I: Iterator<Item=T>,
|
|
|
|
|
where I: IntoIterator<Item=T>,
|
|
|
|
|
T: AsRef<str>
|
|
|
|
|
{
|
|
|
|
|
/// Join an iterator of `str` with a seperator
|
|
|
|
|
fn join(self, with: &str) -> String
|
|
|
|
|
{
|
|
|
|
|
let mut output = String::new();
|
|
|
|
|
let mut first=true;
|
|
|
|
|
for string in self
|
|
|
|
|
for string in self.into_iter()
|
|
|
|
|
{
|
|
|
|
|
if !first {
|
|
|
|
|
output.push_str(with);
|
|
|
|
@ -44,60 +91,99 @@ where I: Iterator<Item=T>,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*macro_rules! typed_swap {
|
|
|
|
|
(@ [] $($reversed:tt)*) => {
|
|
|
|
|
fn swap(self) -> ($($reversed)*);
|
|
|
|
|
};
|
|
|
|
|
(@ [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
|
|
|
|
|
typed_swap!{@ [$($rest)*] $first $($reversed)*}
|
|
|
|
|
};
|
|
|
|
|
(@impl {$($body:tt)*} [] $($reversed:tt)*) => {
|
|
|
|
|
fn swap(self) -> ($($reversed)*)
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct StrChunks<'a, T: ?Sized>(&'a str, usize, PhantomData<&'a T>);
|
|
|
|
|
|
|
|
|
|
impl<'a, T: ?Sized> StrChunks<'a, T>
|
|
|
|
|
{
|
|
|
|
|
/// The rest of the string
|
|
|
|
|
pub fn as_str(&self) -> &'a str
|
|
|
|
|
{
|
|
|
|
|
$($body)*
|
|
|
|
|
&self.0[..]
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
(@impl {$($body:tt)*} [$first:tt $($rest:tt)*] $($reversed:tt)*) => {
|
|
|
|
|
typed_swap!{@impl {$($body)*} [$($rest)*] $first $($reversed)*}
|
|
|
|
|
};
|
|
|
|
|
() => {};
|
|
|
|
|
({$($params:tt)*} $($rest:tt)*) => {
|
|
|
|
|
mod swap {
|
|
|
|
|
pub trait SwapTupleExt<$($params)*>: Sized
|
|
|
|
|
/// The number of chars to break at
|
|
|
|
|
pub fn every(&self) -> usize
|
|
|
|
|
{
|
|
|
|
|
typed_swap!(@ [$($params)*]);
|
|
|
|
|
self.1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<$($params)*> SwapTupleExt<$($params)*> for ($($params)*)
|
|
|
|
|
/// Set the number of chars to break at.
|
|
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
/// Probably don't do this unless you know what you're doing.
|
|
|
|
|
pub fn every_mut(&mut self) -> &mut usize
|
|
|
|
|
{
|
|
|
|
|
typed_swap!(@impl {
|
|
|
|
|
todo!()
|
|
|
|
|
} [$($params)*]);
|
|
|
|
|
&mut self.1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl<'a, T: ?Sized> Iterator for StrChunks<'a, T>
|
|
|
|
|
{
|
|
|
|
|
type Item = &'a str;
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
|
|
|
{
|
|
|
|
|
match self.0.char_indices().nth(self.1).map(|x| x.0) {
|
|
|
|
|
None if self.0.len() > 0 => Some(std::mem::replace(&mut self.0, "")),
|
|
|
|
|
Some(i) => {
|
|
|
|
|
let (left, right) = self.0.split_at(i);
|
|
|
|
|
self.0 = right;
|
|
|
|
|
Some(left)
|
|
|
|
|
},
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typed_swap!($($rest)*);
|
|
|
|
|
}
|
|
|
|
|
pub use swap::*;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(all $first:tt $($params:tt)+) => {
|
|
|
|
|
typed_swap!({$first, $($params),+});
|
|
|
|
|
mod nswap {
|
|
|
|
|
typed_swap!(all $($params)+);
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
let (low, high) = self.0.chars().size_hint();
|
|
|
|
|
(low / self.1, high.map(|x| (x / self.1) + 1)) //not too sure if this is right...
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
(all $($one:tt)?) => {};
|
|
|
|
|
}
|
|
|
|
|
impl<'a, T: ?Sized> std::iter::FusedIterator for StrChunks<'a, T>{}
|
|
|
|
|
|
|
|
|
|
typed_swap!(all A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);
|
|
|
|
|
pub use swap::*;
|
|
|
|
|
/// Split a `str` into chunks on char boundaries
|
|
|
|
|
pub trait ChunkStrsExt
|
|
|
|
|
{
|
|
|
|
|
/// Split this str into a chunking iterator every specified number of chars.
|
|
|
|
|
///
|
|
|
|
|
/// If there are not enough chars left in the string, the rest is returned.
|
|
|
|
|
/// # Note
|
|
|
|
|
/// This operates on codepoints, not bytes.
|
|
|
|
|
fn chunk(&self, every: usize) -> StrChunks<'_, Self>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn test()
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod chunk_tests
|
|
|
|
|
{
|
|
|
|
|
let sw = (1, 2).swap();
|
|
|
|
|
use super::*;
|
|
|
|
|
#[test]
|
|
|
|
|
fn chunk_test()
|
|
|
|
|
{
|
|
|
|
|
let string = r"a2eab409c57a829d23139c61ff2d5e479260c96158ebec0ce4d458afb85b76dłこんな僕は生きてるだけ何万人の人が悲しいんで。!?";
|
|
|
|
|
assert_eq!(&string.chunk(8).join("")[..], string);
|
|
|
|
|
for chunk in string.chunk(8) {
|
|
|
|
|
println!("{}", chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
// ^ unfortunately not lol
|
|
|
|
|
#[test]
|
|
|
|
|
fn group_test()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
let string = r"a2eab409c57a829d23139c61ff2d5e479260c96158ebec0ce4d458afb85b76dłこんな僕は生きてるだけ何万人の人が悲しいんで。!?";
|
|
|
|
|
let astr: String = string.chars().group(8).map(|x| -> String {x.iter().collect()}).collect();
|
|
|
|
|
assert_eq!(&astr[..], string);
|
|
|
|
|
for chunk in string.chars().group(8) {
|
|
|
|
|
println!("{:?}", chunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: AsRef<str>+?Sized> ChunkStrsExt for T
|
|
|
|
|
{
|
|
|
|
|
fn chunk(&self, every: usize) -> StrChunks<'_, Self>
|
|
|
|
|
{
|
|
|
|
|
StrChunks(self.as_ref(), every, PhantomData)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait SwapTupleExt<T,U>: Sized
|
|
|
|
|
{
|
|
|
|
@ -110,11 +196,6 @@ impl<T,U> SwapTupleExt<T,U> for (T,U)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*typed_swap!({A, B}
|
|
|
|
|
{A, U, V}
|
|
|
|
|
{T, U, V, W});*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ASCII_MAP: [char; 256] = [
|
|
|
|
|
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
|
|
|
|
|
'.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.',
|
|
|
|
|