tuplez 0.14.14-alpha

Tuples represented in recursive form
Documentation
//! Provides the ability to traverse tuples.
//!
//! Check the documentation page of [`Mapper`] for details.
use crate::{Tuple, TupleLike, Unit};

/// Define mappers for traversing the tuple.
///
/// To traverse a tuple with type [`Tuple<T0, T1, ... Tn>`](crate::Tuple), you need to construct a custom mapper type,
/// which implements [`Mapper<T0>`], [`Mapper<T1>`] ... [`Mapper<Tn>`].
/// Pass your mapper to tuple's [`foreach()`](TupleLike::foreach()) method, then the tuple will call
/// mapper's [`map()`](Mapper::map()) method in order of its elements and move the elements in.
///
/// NOTE: Traversing a tuple will consume it. If this is not what you want, call [`as_ref()`](TupleLike::as_ref())
/// or [`as_mut()`](TupleLike::as_mut()) to create a new tuple that references its all members before traversing.
///
/// Tip: [`Mapper`] map elements by their types. If you are looking for a way to map elements by their order,
/// then what you are looking for is to
/// [pass a tuple containing callable objects into `fold()` method](Tuple#fold-tuples-in-order-of-their-elements-but-collecting-results-in-a-tuple).
///
/// # Quickly build a mapper by macros
///
/// Here are two ways you can quickly build a folder.
///
/// ## Traverse tuples by element types
///
/// The [`mapper!`](crate::mapper!) macro helps you build a mapper that traverses tuples according to their element types.
///
/// For example:
///
/// ```
/// use tuplez::{mapper, tuple, TupleLike};
///
/// let tup = tuple!(1, "hello", 3.14).foreach(mapper! {
///     |x: i32| -> i64 { x as i64 }
///     |x: f32| -> String { x.to_string() }
///     <'a> |x: &'a str| -> &'a [u8] { x.as_bytes() }
/// });
/// assert_eq!(tup, tuple!(1i64, b"hello" as &[u8], "3.14".to_string()));
/// ```
///
/// ## Traverse tuples in order of their elements
///
/// You can create a new tuple with the same number of elements, whose elements are all callable objects that accepts an element
/// and returns another value ([`FnOnce(T) -> U`](std::ops::FnOnce)), then, you can use that tuple as a mapper.

///
/// ```
/// use tuplez::{tuple, TupleLike};
///
/// let tup = tuple!(1, 2, 3);
/// let result = tup.foreach(
///     tuple!(
///         |x| x as f32,
///         |x: i32| x.to_string(),
///         |x: i32| Some(x),
///     )
/// );
/// assert_eq!(result, tuple!(1.0, "2".to_string(), Some(3)));
/// ```
///
/// # Custom mapper
///
/// For more complex cases that cannot be covered by the [`mapper!`](crate::mapper!) macro,
/// for example, you want to save some results into context variables,
/// you need to implement [`Mapper<Ti>`] for your mapper for all element type `Ti`s in tuples.
/// Generic can be used.
///
/// For example:
///
/// ```
/// use tuplez::{foreach::Mapper, tuple, TupleLike};
///
/// struct MyElement(i32);
///
/// struct Collector<'a>(&'a mut Vec<String>);
///
/// impl<T: ToString> Mapper<&T> for Collector<'_> {
///     type Output = ();
///     type NextMapper = Self;
///     fn map(self, value: &T) -> (Self::Output, Self::NextMapper) {
///         (
///             self.0.push(format!(
///                 "{} : {}",
///                 std::any::type_name::<T>(),
///                 value.to_string()
///             )),
///             self,
///         )
///     }
/// }
///
/// impl Mapper<&MyElement> for Collector<'_> {
///     type Output = ();
///     type NextMapper = Self;
///     fn map(self, value: &MyElement) -> (Self::Output, Self::NextMapper) {
///         (self.0.push(format!("MyElement : {}", value.0)), self)
///     }
/// }
///
/// let mut buffers = vec![];
/// let collector = Collector(&mut buffers);
/// tuple!(1, "hello", MyElement(14))
///     .as_ref()
///     .foreach(collector);
/// assert_eq!(
///     buffers,
///     vec![
///         "i32 : 1".to_string(),
///         "&str : hello".to_string(),
///         "MyElement : 14".to_string()
///     ]
/// );
/// ```
pub trait Mapper<T> {
    /// Output type of mapping.
    type Output;

    /// Type of next mapper to be use.
    type NextMapper;

    /// Map an element to another value.
    fn map(self, value: T) -> (Self::Output, Self::NextMapper);
}

/// Traverse the tuple.
///
/// # The mapper `F`
///
/// For traversing [`Tuple<T0, T1, ... Tn>`](crate::Tuple), you need to build a mapper,
/// which needs to implement [`Mapper<T0>`], [`Mapper<T1>`] ... [`Mapper<Tn>`].
///
/// See the documentation page of [`Mapper`] for details.
pub trait Foreach<F>: TupleLike {
    /// The type of tuple generated by traversing the tuple.
    type Output: TupleLike;

    /// Traverse the tuple, and collect the output of traversal into a new tuple.
    ///
    /// NOTE: Traversing a tuple will consume it. If this is not what you want, call [`as_ref()`](TupleLike::as_ref())
    /// or [`as_mut()`](TupleLike::as_mut()) to create a new tuple that references its all members before traversing.
    ///
    /// Hint: The [`TupleLike`] trait provides the [`foreach()`](TupleLike::foreach()) method as the wrapper
    /// for this [`foreach()`](Foreach::foreach()) method.
    ///
    /// # Example
    ///
    /// ```
    /// use tuplez::{mapper, tuple, TupleLike};
    ///
    /// let tup = tuple!(1, "hello", 3.14).foreach(mapper! {
    ///     |x: i32| -> i64 { x as i64 }
    ///     |x: f32| -> String { x.to_string() }
    ///     <'a> |x: &'a str| -> &'a [u8] { x.as_bytes() }
    /// });
    /// assert_eq!(tup, tuple!(1i64, b"hello" as &[u8], "3.14".to_string()));
    /// ```
    fn foreach(self, f: F) -> Self::Output;
}

impl<First, F, Out, FOthers> Mapper<First> for Tuple<F, FOthers>
where
    F: FnOnce(First) -> Out,
    FOthers: TupleLike,
{
    type Output = Out;
    type NextMapper = FOthers;

    fn map(self, value: First) -> (Self::Output, Self::NextMapper) {
        ((self.0)(value), self.1)
    }
}

impl<F> Foreach<F> for Unit {
    type Output = Unit;
    fn foreach(self, _: F) -> Self::Output {
        Unit
    }
}

impl<F, First, Other> Foreach<F> for Tuple<First, Other>
where
    F: Mapper<First>,
    Other: Foreach<F::NextMapper>,
{
    type Output = Tuple<F::Output, Other::Output>;
    fn foreach(self, f: F) -> Self::Output {
        let (out, f) = f.map(self.0);
        Tuple(out, Foreach::foreach(self.1, f))
    }
}