rspl 0.1.3

A stream processor language.
Documentation
//! This module defines streams of some type intensionally by means of a trait.
//! Additionally, the module declares submodules with implementations of the trait.

pub mod infinite_lists;

#[cfg(feature = "std")]
pub mod overeager_receivers;

/// A characterization of streams of some type `X`: a stream of `X` is an object from which one can observe an item of type `X` (the head of the stream) or another stream of `X` (the tail of the stream).
pub trait Stream<X> {
    /// Returns a reference to the first item of `self`.
    fn head(&self) -> &X;
    /// Discards the first item of `self` and returns the remainder of the stream.
    ///
    /// # Panics
    ///
    /// Implementations may choose to panic.
    #[must_use]
    fn tail(self) -> Self;
}

/// Prints a specified number of elements from the provided stream and returns the remaining part.
/// - `stream` is the stream to be printed.
/// - `n` is the number of elements to be printed.
///
/// # Panics
///
/// Panics if the respective implementation of [`Stream`] panics.
///
/// # Notes
///
/// Note that this function can block the current thread if the implementation of [`Stream`] does.
/// Even if it does not block, it may be slow as it uses `println!` in a loop.
///
/// # Examples
///
/// Printing the first five elements of the infinite list `0, 1, 2, ..., |usize|, 0, 1, ...`:
///
/// ```
/// use rspl::streams::infinite_lists::InfiniteList;
///
/// fn ascending<'a>(n: usize) -> InfiniteList<'a, usize> {
///     InfiniteList::Cons(n, Box::new(move || ascending(n + 1)))
/// }
///
/// rspl::streams::print(ascending(0), 5);
/// ```
#[cfg(feature = "std")]
pub fn print<X, S>(mut stream: S, n: usize) -> S
where
    S: Stream<X>,
    X: std::fmt::Display,
{
    for _ in 0..n {
        println!("{}", stream.head());
        stream = stream.tail();
    }

    stream
}

#[cfg(test)]
mod tests {
    #[cfg(feature = "std")]
    use super::*;

    #[cfg(feature = "std")]
    use infinite_lists::InfiniteList;

    #[macro_export]
    macro_rules! assert_head_eq {
        ($stream:expr, $x:expr) => {
            assert_eq!(*$stream.head(), $x);
        };
    }

    #[macro_export]
    macro_rules! assert_tail_starts_with {
        ($stream:expr, $xs:expr) => {
            for x in $xs {
                $stream = $stream.tail();
                assert_head_eq!($stream, x);
            }
        };
    }

    #[cfg(feature = "std")]
    #[test]
    fn test_print() {
        let stream = InfiniteList::cons(false, || {
            InfiniteList::cons(false, || {
                InfiniteList::cons(true, || InfiniteList::constant(true))
            })
        });

        let stream = print(stream, 2);
        assert!(stream.head());
    }
}