transform/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
// LIB.rs
// by Lut99
//
// Created:
// 31 Oct 2023, 16:07:42
// Last edited:
// 31 Oct 2023, 16:44:24
// Auto updated?
// Yes
//
// Description:
//! A small Rust library that defined the [`Transform`] iterator, which
//! can map an element in a tuple to zero or more elements of a
//! potentially different type.
//!
//! # Installation
//! To install the `transform`-crate, simply add it to your `Cargo.toml` file:
//! ```toml
//! transform = { git = "https://github.com/Lut99/transform-rs" }
//! ```
//!
//! You can also commit yourself to a particular version by using the `tag`-key.
//! ```toml
//! transform = { git = "https://github.com/Lut99/transform-rs", tag = "v0.1.0" }
//! ```
//!
//!
//! # Usage
//! To use this library, first add the `Transform`-trait to your current scope:
//! ```rust
//! use transform::Transform as _;
//! // Or, if preferred:
//! use transform::prelude::*;
//!
//! // ...
//! ```
//! Next, you can call `transform()` on iterator functions to do things like content-based expansion:
//! ```rust
//! let numbers = vec![1, 2, 3, 4, 5];
//! let numbers: Vec<i32> = numbers.into_iter().transform(|num| vec![num; num as usize]).collect();
//! assert_eq!(numbers, vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
//! ```
//!
//!
//! # Features
//! This crate does not have any features.
//!
//!
//! # License
//! This project is licensed under GPLv3. See [`LICENSE`](./LICENSE) for more information.
//!
//!
//! # Contributing
//! If you want to contribute to this create, welcome! Feel free to [raise an issue](https://github.com/Lut99/transform-rs/issues) or [create a pull request](https://github.com/Lut99/transform-rs/pulls).
//!
//! Note, however, that this is a hobby project. As such, I might not adopt all suggestions, no matter how good they are ;)
//
// Defines a convenient place for importing traits
pub mod prelude {
pub use super::Transform;
}
/***** LIBRARY *****/
/// An iterator that applies a closure that can transform an element in the existing iterator into zero or more elements in the resulting iterator.
///
/// Note that the closure is _not_ called for the newly produces elements, only for those in the old iterator.
///
/// # Examples
/// ```rust
/// use transform::Transform as _;
///
/// // Transform can be used for content-based expansion
/// let numbers = vec![1, 2, 3, 4, 5];
/// let numbers: Vec<i32> = numbers.into_iter().transform(|num| vec![num; num as usize]).collect();
/// assert_eq!(numbers, vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
///
/// // We can also clone elements while doing so
/// let numbers = vec![1, 2, 3, 4, 5];
/// let numbers: Vec<i32> = numbers.iter().transform(|num| vec![*num; *num as usize]).collect();
/// assert_eq!(numbers, vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
///
/// // Or change data types altogether
/// let chars = vec!['a', 'b', 'c'];
/// let padded_chars: Vec<u8> = chars.into_iter().transform(|c| [0, 0, 0, c as u8]).collect();
/// assert_eq!(padded_chars, vec![0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99]);
/// ```
pub struct TransformIter<I, F, R>
where
I: Iterator,
F: FnMut(I::Item) -> R,
R: IntoIterator,
{
/// The underlying iterator to apply transform over.
iter: I,
/// The closure to apply on elements of `I`
func: F,
/// The buffer of elements to flush first.
buf: Option<R::IntoIter>,
}
impl<I, F, R> Iterator for TransformIter<I, F, R>
where
I: Iterator,
F: FnMut(I::Item) -> R,
R: IntoIterator,
{
type Item = R::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
// Attempt to flush the buffer first
if let Some(next) = self.buf.as_mut().map(|buf| buf.next()).flatten() {
return Some(next);
}
// Else, attempt to get the next element from the iterator
let next: I::Item = match self.iter.next() {
Some(next) => next,
None => return None,
};
// If we haven't got anything in the buffer but we do have an item in the iterator, then get the next buffer
self.buf = Some((self.func)(next).into_iter());
// NOTE: The recursive call is mandatory to properly deal with elements that are empty (we need to call the iterator right away)
self.next()
}
}
/// A trait that adds the [`transform()`](Transform::transform())-function to all [`Iterator`]s.
///
/// This function applies a closure that can transform an element in the existing iterator into zero or more elements in the resulting iterator.
///
/// Note that the closure is _not_ called for the newly produces elements, only for those in the old iterator.
///
/// # Examples
/// ```rust
/// use transform::Transform as _;
///
/// // Transform can be used for content-based expansion
/// let numbers = vec![1, 2, 3, 4, 5];
/// let numbers: Vec<i32> = numbers.into_iter().transform(|num| vec![num; num as usize]).collect();
/// assert_eq!(numbers, vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
///
/// // We can also clone elements while doing so
/// let numbers = vec![1, 2, 3, 4, 5];
/// let numbers: Vec<i32> = numbers.iter().transform(|num| vec![*num; *num as usize]).collect();
/// assert_eq!(numbers, vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5]);
///
/// // Or change data types altogether
/// let chars = vec!['a', 'b', 'c'];
/// let padded_chars: Vec<u8> = chars.into_iter().transform(|c| [0, 0, 0, c as u8]).collect();
/// assert_eq!(padded_chars, vec![0, 0, 0, 97, 0, 0, 0, 98, 0, 0, 0, 99]);
/// ```
pub trait Transform: Iterator {
/// Transforms each element in the iterator into zero or more elements of a (potentially) different type.
///
/// # Arguments
/// - `predicate`: A closure that transforms each element in the iterator into an iterator that produces the zero-or-more elements of another type.
///
/// # Returns
/// A new [`TransformIter`] that can do as advertised.
fn transform<F, R>(self, predicate: F) -> TransformIter<Self, F, R>
where
Self: Sized,
F: FnMut(Self::Item) -> R,
R: IntoIterator,
{
TransformIter { iter: self, func: predicate, buf: None }
}
}
impl<T: Iterator> Transform for T {}