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 {}