diff --git a/to/README.md b/to/README.md index 5c34874..9043652 100644 --- a/to/README.md +++ b/to/README.md @@ -17,13 +17,14 @@ use rosenpass_to::{to, with_destination, To}; use std::ops::BitXorAssign; // Destination functions return some value that implements the To trait. -// Unfortunately dealing with lifetimes is a bit more finicky than it would# +// Unfortunately dealing with lifetimes is a bit more finicky than it would // be without destination parameters fn xor_slice<'a, T>(src: &'a [T]) -> impl To<[T], ()> + 'a where T: BitXorAssign + Clone, { // Custom implementations of the to trait can be created, but the easiest + // way to create them is to use the provided helper functions like with_destination. with_destination(move |dst: &mut [T]| { assert!(src.len() == dst.len()); for (d, s) in dst.iter_mut().zip(src.iter()) { diff --git a/to/src/ops.rs b/to/src/ops.rs index 385c477..0c0973f 100644 --- a/to/src/ops.rs +++ b/to/src/ops.rs @@ -1,4 +1,5 @@ -//! Functions with destination copying data between slices and arrays. +//! Functions that make it easy to copy data between arrays and slices using functions with +//! destinations. See the specific functions for examples and more explanations. use crate::{with_destination, To}; @@ -8,6 +9,17 @@ use crate::{with_destination, To}; /// # Panics /// /// This function will panic if the two slices have different lengths. +/// +/// # Example +/// ``` +/// use rosenpass_to::To; +/// # use crate::rosenpass_to::ops::copy_slice; +/// let to_function = copy_slice(&[0; 16]); +/// let mut dst = [255; 16]; +/// to_function.to(&mut dst); +/// // After the operation `dst` will hold the same data as the original slice. +/// assert!(dst.iter().all(|b| *b == 0)); +/// ``` pub fn copy_slice(origin: &[T]) -> impl To<[T], ()> + '_ where T: Copy, @@ -23,6 +35,19 @@ where /// # Panics /// /// This function will panic if destination is shorter than origin. +/// +/// # Example +/// ``` +/// use rosenpass_to::To; +/// # use crate::rosenpass_to::ops::copy_slice_least_src; +/// let to_function = copy_slice_least_src(&[0; 16]); +/// let mut dst = [255; 32]; +/// to_function.to(&mut dst); +/// // After the operation the first half of `dst` will hold the same data as the original slice. +/// assert!(dst[0..16].iter().all(|b| *b == 0)); +/// // The second half will have remained the same +/// assert!(dst[16..32].iter().all(|b| *b == 255)); +/// ``` pub fn copy_slice_least_src(origin: &[T]) -> impl To<[T], ()> + '_ where T: Copy, @@ -34,6 +59,18 @@ where /// destination. /// /// Copies as much data as is present in the shorter slice. +/// # Example +/// ``` +/// use rosenpass_to::To; +/// # use crate::rosenpass_to::ops::copy_slice_least; +/// let to_function = copy_slice_least(&[0; 16]); +/// let mut dst = [255; 32]; +/// to_function.to(&mut dst); +/// // After the operation the first half of `dst` will hold the same data as the original slice. +/// assert!(dst[0..16].iter().all(|b| *b == 0)); +/// // The second half will have remained the same. +/// assert!(dst[16..32].iter().all(|b| *b == 255)); +/// ``` pub fn copy_slice_least(origin: &[T]) -> impl To<[T], ()> + '_ where T: Copy, @@ -47,6 +84,24 @@ where /// Function with destination that attempts to copy data from origin into the destination. /// /// Will return None if the slices are of different lengths. +/// # Example +/// ``` +/// use rosenpass_to::To; +/// # use crate::rosenpass_to::ops::try_copy_slice; +/// let to_function = try_copy_slice(&[0; 16]); +/// let mut dst = [255; 32]; +/// let result = to_function.to(&mut dst); +/// // This will return None because the slices do not have the same length. +/// assert!(result.is_none()); +/// +/// let to_function = try_copy_slice(&[0; 16]); +/// let mut dst = [255; 16]; +/// let result = to_function.to(&mut dst); +/// // This time it works: +/// assert!(result.is_some()); +/// // After the operation `dst` will hold the same data as the original slice. +/// assert!(dst.iter().all(|b| *b == 0)); +/// ``` pub fn try_copy_slice(origin: &[T]) -> impl To<[T], Option<()>> + '_ where T: Copy, @@ -62,6 +117,26 @@ where /// Destination may be longer than origin. /// /// Will return None if the destination is shorter than origin. +/// # Example +/// ```rust +/// use rosenpass_to::To; +/// # use crate::rosenpass_to::ops::try_copy_slice_least_src; +/// let to_function = try_copy_slice_least_src(&[0; 16]); +/// let mut dst = [255; 15]; +/// let result = to_function.to(&mut dst); +/// // This will return None because the destination is to short. +/// assert!(result.is_none()); +/// +/// let to_function = try_copy_slice_least_src(&[0; 16]); +/// let mut dst = [255; 32]; +/// let result = to_function.to(&mut dst); +/// // This time it works: +/// assert!(result.is_some()); +/// // After the operation, the first half of `dst` will hold the same data as the original slice. +/// assert!(dst[0..16].iter().all(|b| *b == 0)); +/// // The second half will have remained the same. +/// assert!(dst[16..32].iter().all(|b| *b == 255)); +/// ``` pub fn try_copy_slice_least_src(origin: &[T]) -> impl To<[T], Option<()>> + '_ where T: Copy, @@ -72,6 +147,18 @@ where } /// Function with destination that copies all data between two array references. +/// +/// # Example +/// ```rust +/// use rosenpass_to::ops::copy_array; +/// use rosenpass_to::To; +/// let my_arr: [u8; 32] = [0; 32]; +/// let to_function = copy_array(&my_arr); +/// let mut dst = [255; 32]; +/// to_function.to(&mut dst); +/// // After the operation `dst` will hold the same data as the original slice. +/// assert!(dst.iter().all(|b| *b == 0)); +/// ``` pub fn copy_array(origin: &[T; N]) -> impl To<[T; N], ()> + '_ where T: Copy, diff --git a/to/src/to/beside.rs b/to/src/to/beside.rs index 8473dfe..b4bf84a 100644 --- a/to/src/to/beside.rs +++ b/to/src/to/beside.rs @@ -1,6 +1,11 @@ +//! This module provides the [Beside] struct. In the context of functions with targets, +//! [Beside] structures the destination value and the return value unmistakably and offers useful +//! helper functions to work with them. + use crate::CondenseBeside; -/// Named tuple holding the return value and the output from a function with destinations. +/// Named tuple holding the return value and the destination from a function with destinations. +/// See the respective functions for usage examples. #[derive(Debug, PartialEq, Eq, Default, PartialOrd, Ord, Copy, Clone)] pub struct Beside(pub Val, pub Ret); @@ -59,7 +64,7 @@ impl Beside { &mut self.1 } - /// Perform beside condensation. See [CondenseBeside] + /// Perform beside condensation. See [CondenseBeside] for more details. /// /// # Example /// ``` @@ -90,3 +95,25 @@ impl From> for (Val, Ret) { (val, ret) } } + +#[cfg(test)] +mod tests { + use crate::Beside; + + #[test] + fn from_tuple() { + let tuple = (21u8, 42u16); + let beside: Beside = Beside::from(tuple); + assert_eq!(beside.dest(), &21u8); + assert_eq!(beside.ret(), &42u16); + } + + #[test] + fn from_beside() { + let beside: Beside = Beside(21u8, 42u16); + type U8u16 = (u8, u16); + let tuple = U8u16::from(beside); + assert_eq!(tuple.0, 21u8); + assert_eq!(tuple.1, 42u16); + } +} diff --git a/to/src/to/condense.rs b/to/src/to/condense.rs index b9df4ba..04f834b 100644 --- a/to/src/to/condense.rs +++ b/to/src/to/condense.rs @@ -1,4 +1,11 @@ -/// Beside condensation. +//! This module provides condensation for values that stand side by side, +//! which is often useful when working with destination parameters. See [CondenseBeside] +//! for more details. + +/// Condenses two values that stand beside each other into one value. +/// For example, a blanked implementation for [Result<(), Error>](Result) is provided. If +/// `condense(val)` is called on such an object, a [Result](Result) will +/// be returned, if `val` is of type `Val`. /// /// This trait can be used to enable the use of [to_this(|| ...)](crate::To::to_this), /// [to_value()](crate::To::to_value), and [collect::<...>()](crate::To::collect) with custom @@ -6,6 +13,19 @@ /// /// The function [Beside::condense()](crate::Beside::condense) is a shorthand for using the /// condense trait. +/// +/// # Example +/// As an example implementation, we take a look at the blanket implementation for [Option] +/// ```ignore +/// impl CondenseBeside for Option<()> { +/// type Condensed = Option; +/// +/// /// Replaces the empty tuple inside this [Option] with `ret`. +/// fn condense(self, ret: Val) -> Option { +/// self.map(|()| ret) +/// } +/// } +/// ``` pub trait CondenseBeside { /// The type that results from condensation. type Condensed; @@ -17,6 +37,7 @@ pub trait CondenseBeside { impl CondenseBeside for () { type Condensed = Val; + /// Replaces this empty tuple with `ret`. fn condense(self, ret: Val) -> Val { ret } @@ -25,6 +46,7 @@ impl CondenseBeside for () { impl CondenseBeside for Result<(), Error> { type Condensed = Result; + /// Replaces the empty tuple inside this [Result] with `ret`. fn condense(self, ret: Val) -> Result { self.map(|()| ret) } @@ -33,6 +55,7 @@ impl CondenseBeside for Result<(), Error> { impl CondenseBeside for Option<()> { type Condensed = Option; + /// Replaces the empty tuple inside this [Option] with `ret`. fn condense(self, ret: Val) -> Option { self.map(|()| ret) } diff --git a/to/src/to/dst_coercion.rs b/to/src/to/dst_coercion.rs index fec6db5..ea4f0dc 100644 --- a/to/src/to/dst_coercion.rs +++ b/to/src/to/dst_coercion.rs @@ -1,5 +1,28 @@ -/// Helper performing explicit unsized coercion. -/// Used by the [to](crate::to()) function. +//! This module provides explicit type coercion from [Sized] types to [?Sized][core::marker::Sized] +//! types. See [DstCoercion] for more details. + +/// Helper Trait for performing explicit coercion from [Sized] types to +/// [?Sized][core::marker::Sized] types. It's used by the [to](crate::to()) function. +/// +/// We provide blanket implementations for any [Sized] type and for any array of [Sized] types. +/// +/// # Example +/// It can be used as follows: +/// ``` +/// # use rosenpass_to::DstCoercion; +/// // Consider a sized type like this example: +/// struct SizedStruct { +/// x: u32 +/// } +/// // Then we can coerce it to be unsized: +/// let mut sized = SizedStruct { x: 42 }; +/// assert_eq!(42, sized.coerce_dest().x); +/// +/// // Analogously, we can coerce arrays to slices: +/// let mut sized_array = [1, 2, 3, 4, 5, 6, 7, 8, 9]; +/// let un_sized: &[i32] = sized_array.coerce_dest(); +/// assert_eq!(un_sized, un_sized); +/// ``` pub trait DstCoercion { /// Performs an explicit coercion to the destination type. fn coerce_dest(&mut self) -> &mut Dst; diff --git a/to/src/to/mod.rs b/to/src/to/mod.rs index dfe4e52..093a84f 100644 --- a/to/src/to/mod.rs +++ b/to/src/to/mod.rs @@ -6,11 +6,16 @@ //! - `Dst: ?Sized`; (e.g. [u8]) – The target to write to //! - `Out: Sized = &mut Dst`; (e.g. &mut [u8]) – A reference to the target to write to //! - `Coercable: ?Sized + DstCoercion`; (e.g. `[u8]`, `[u8; 16]`) – Some value that -//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or some sized variant of +//! destination coercion can be applied to. Usually either `Dst` itself (e.g. `[u8]` or +//! some sized variant of //! `Dst` (e.g. `[u8; 64]`). -//! - `Ret: Sized`; (anything) – must be `CondenseBeside<_>` if condensing is to be applied. The ordinary return value of a function with an output -//! - `Val: Sized + BorrowMut`; (e.g. [u8; 16]) – Some owned storage that can be borrowed as `Dst` -//! - `Condensed: Sized = CondenseBeside::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) – The combiation of Val and Ret after condensing was applied (`Beside::condense()`/`Ret::condense(v)` for all `v : Val`). +//! - `Ret: Sized`; (anything) – must be `CondenseBeside<_>` if condensing is to be applied. The +//! ordinary return value of a function with an output +//! - `Val: Sized + BorrowMut`; (e.g. [u8; 16]) – Some owned storage that can be borrowed as +//! `Dst` +//! - `Condensed: Sized = CondenseBeside::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) +//! – The combiation of Val and Ret after condensing was applied +//! (`Beside::condense()`/`Ret::condense(v)` for all `v : Val`). pub mod beside; pub mod condense; diff --git a/to/src/to/to_function.rs b/to/src/to/to_function.rs index dfb01b5..f7dd0b9 100644 --- a/to/src/to/to_function.rs +++ b/to/src/to/to_function.rs @@ -1,9 +1,23 @@ +//! This module provides the [To::to] function which allows to use functions with destination in +//! a manner akin to that of a variable assignment. See [To::to] for more details. + use crate::{DstCoercion, To}; /// Alias for [To::to] moving the destination to the left. /// /// This provides similar haptics to the let assignment syntax is rust, which also keeps /// the variable to assign to on the left and the generating function on the right. +/// +/// # Example +/// ```rust +/// // Using the to function to have data flowing from the right to the left, +/// // performing something akin to a variable assignment. +/// use rosenpass_to::ops::copy_slice_least; +/// # use rosenpass_to::to; +/// let mut dst = b" ".to_vec(); +/// to(&mut dst[..], copy_slice_least(b"Hello World")); +/// assert_eq!(&dst[..], b"Hello World"); +/// ``` pub fn to(dst: &mut Coercable, src: Src) -> Ret where Coercable: ?Sized + DstCoercion, diff --git a/to/src/to/to_trait.rs b/to/src/to/to_trait.rs index 63ad177..86325cc 100644 --- a/to/src/to/to_trait.rs +++ b/to/src/to/to_trait.rs @@ -1,12 +1,45 @@ +//! TODO: Mod docu + use crate::{Beside, CondenseBeside}; use std::borrow::BorrowMut; /// The To trait is the core of the to crate; most functions with destinations will either return -/// an object that is an instance of this trait or they will return `-> impl To impl To`. /// /// A quick way to implement a function with destination is to use the -/// [with_destination(|param: &mut Type| ...)] higher order function. +/// [with_destination(|param: &mut Type| ...)](crate::with_destination) higher order function. +/// +/// # Example +/// Below, we provide a very simple example for how the Trait can be implemented. More examples for +/// how this Trait is best implemented can be found in the overall [crate documentation](crate). +/// ``` +/// use rosenpass_to::To; +/// +/// // This is a simple wrapper around a String that can be written into a byte array using to. +/// struct StringToBytes { +/// inner: String +/// } +/// +/// impl To<[u8], Result<(), String>> for StringToBytes { +/// fn to(self, out: &mut [u8]) -> Result<(), String> { +/// let bytes = self.inner.as_bytes(); +/// if bytes.len() > out.len() { +/// return Err("out is to short".to_string()); +/// } +/// for i in 0..bytes.len() { +/// (*out)[i] = bytes[i]; +/// } +/// Ok(()) +/// } +/// } +/// +/// let string_to_bytes = StringToBytes { inner: "my message".to_string() }; +/// let mut buffer: [u8; 10] = [0; 10]; +/// let result = string_to_bytes.to(&mut buffer); +/// assert_eq!(buffer, [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]); +/// assert!(result.is_ok()); +/// ``` pub trait To: Sized { /// Writes self to the destination `out` and returns a value of type `Ret`. /// @@ -19,6 +52,36 @@ pub trait To: Sized { /// calls [crate::to()] to evaluate the function and finally /// returns a [Beside] instance containing the generated destination value and the return /// value. + /// + /// # Example + /// Below, we rewrite the example for the overall [To]-Trait and simplify it by using + /// [self.to_this_beside]. We refer to the overall [crate documentation](crate) + /// for more examples and general explanations. + /// ``` + /// # use rosenpass_to::To; + /// use rosenpass_to::Beside; + /// # struct StringToBytes { + /// # inner: String + /// # } + /// + /// # impl To<[u8], Result<(), String>> for StringToBytes { + /// # fn to(self, out: &mut [u8]) -> Result<(), String> { + /// # let bytes = self.inner.as_bytes(); + /// # if bytes.len() > out.len() { + /// # return Err("out is to short".to_string()); + /// # } + /// # for i in 0..bytes.len() { + /// # (*out)[i] = bytes[i]; + /// # } + /// # Ok(()) + /// # } + /// # } + /// // StringToBytes is taken from the overall Trait example. + /// let string_to_bytes = StringToBytes { inner: "my message".to_string() }; + /// let Beside(dst, result) = string_to_bytes.to_this_beside(|| [0; 10]); + /// assert_eq!(dst, [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]); + /// assert!(result.is_ok()); + /// ``` fn to_this_beside(self, fun: Fun) -> Beside where Val: BorrowMut, @@ -31,10 +94,21 @@ pub trait To: Sized { /// Generate a destination on the fly using default. /// - /// Uses [Default] to create a value, - /// calls [crate::to()] to evaluate the function and finally + /// Uses [Default] to create a value, calls [crate::to()] to evaluate the function and finally /// returns a [Beside] instance containing the generated destination value and the return /// value. + /// + /// # Example + /// Below, we provide a simple example for the usage of [to_value_beside](To::to_value_beside). + /// We refer to the overall [crate documentation](crate) for more examples and general + /// explanations. + /// ``` + /// use rosenpass_to::Beside; + /// use rosenpass_to::To; + /// use rosenpass_to::ops::*; + /// let Beside(dst, ret) = copy_array(&[42u8; 16]).to_value_beside(); + /// assert_eq!(dst, [42u8; 16]); + /// ``` fn to_value_beside(self) -> Beside where Dst: Sized + Default, @@ -53,6 +127,19 @@ pub trait To: Sized { /// when the Destination is unsized. /// /// This could be the case when the destination is an `[u8]` for instance. + /// + /// # Example + /// Below, we provide a simple example for the usage of [collect_beside](To::collect_beside). + /// We refer to the overall [crate documentation](crate) for more examples and general + /// explanations. + /// ``` + /// use rosenpass_to::Beside; + /// use rosenpass_to::To; + /// use rosenpass_to::ops::*; + /// + /// let Beside(dst, ret) = copy_slice(&[42u8; 16]).collect_beside::<[u8; 16]>(); + /// assert_eq!(dst, [42u8; 16]); + /// ``` fn collect_beside(self) -> Beside where Val: Default + BorrowMut, @@ -64,6 +151,36 @@ pub trait To: Sized { /// return value into one. /// /// This is like using [Self::to_this_beside] followed by calling [Beside::condense]. + /// # Example + /// Below, we rewrite the example for the overall [To]-Trait and simplify it by using + /// [Self::to_this]. We refer to the overall [crate documentation](crate) + /// for more examples and general explanations. + /// ``` + /// # use rosenpass_to::To; + /// use rosenpass_to::Beside; + /// # struct StringToBytes { + /// # inner: String + /// # } + /// + /// # impl To<[u8], Result<(), String>> for StringToBytes { + /// # fn to(self, out: &mut [u8]) -> Result<(), String> { + /// # let bytes = self.inner.as_bytes(); + /// # if bytes.len() > out.len() { + /// # return Err("out is to short".to_string()); + /// # } + /// # for i in 0..bytes.len() { + /// # (*out)[i] = bytes[i]; + /// # } + /// # Ok(()) + /// # } + /// # } + /// // StringToBytes is taken from the overall Trait example. + /// let string_to_bytes = StringToBytes { inner: "my message".to_string() }; + /// let result = string_to_bytes.to_this_beside(|| [0; 10]).condense(); + /// assert!(result.is_ok()); + /// assert_eq!(result.unwrap(), [109, 121, 32, 109, 101, 115, 115, 97, 103, 101]); + /// + /// ``` fn to_this(self, fun: Fun) -> >::Condensed where Ret: CondenseBeside, @@ -77,6 +194,18 @@ pub trait To: Sized { /// return value into one. /// /// This is like using [Self::to_value_beside] followed by calling [Beside::condense]. + /// + /// # Example + /// Below, we provide a simple example for the usage of [to_value](To::to_value). + /// We refer to the overall [crate documentation](crate) for more examples and general + /// explanations. + /// ``` + /// use rosenpass_to::Beside; + /// use rosenpass_to::To; + /// use rosenpass_to::ops::*; + /// let dst = copy_array(&[42u8; 16]).to_value_beside().condense(); + /// assert_eq!(dst, [42u8; 16]); + /// ``` fn to_value(self) -> >::Condensed where Dst: Sized + Default, @@ -89,6 +218,19 @@ pub trait To: Sized { /// return value into one. /// /// This is like using [Self::collect_beside] followed by calling [Beside::condense]. + /// + /// # Example + /// Below, we provide a simple example for the usage of [collect](To::collect). + /// We refer to the overall [crate documentation](crate) for more examples and general + /// explanations. + /// ``` + /// use rosenpass_to::Beside; + /// use rosenpass_to::To; + /// use rosenpass_to::ops::*; + /// + /// let dst = copy_slice(&[42u8; 16]).collect_beside::<[u8; 16]>().condense(); + /// assert_eq!(dst, [42u8; 16]); + /// ``` fn collect(self) -> >::Condensed where Val: Default + BorrowMut, diff --git a/to/src/to/with_destination.rs b/to/src/to/with_destination.rs index 6fbec1e..eab2ea0 100644 --- a/to/src/to/with_destination.rs +++ b/to/src/to/with_destination.rs @@ -1,15 +1,19 @@ +//! The module provides the [with_destination] function, which makes it easy to create +//! a [To] from a lambda function. See [with_destination] and the [crate documentation](crate) +//! for more details and examples. + use crate::To; use std::marker::PhantomData; -/// A struct that wraps a closure and implements the `To` trait +/// A struct that wraps a closure and implements the `To` trait. /// /// This allows passing closures that operate on a destination type `Dst` -/// and return `Ret`. +/// and return `Ret`. It is only internally used to implement [with_destination]. /// /// # Type Parameters -/// * `Dst` - The destination type the closure operates on -/// * `Ret` - The return type of the closure -/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret` +/// * `Dst` - The destination type the closure operates on. +/// * `Ret` - The return type of the closure. +/// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret`. struct ToClosure where Dst: ?Sized, @@ -17,11 +21,11 @@ where { /// The function to call. fun: Fun, - /// Phantom data to hold the destination type + /// Phantom data to hold the destination type. _val: PhantomData>, } -/// Implementation of the `To` trait for ToClosure +/// Implementation of the `To` trait for ToClosure. /// /// This enables calling the wrapped closure with a destination reference. impl To for ToClosure @@ -33,6 +37,7 @@ where /// /// # Arguments /// * `out` - Mutable reference to the destination + /// See the tutorial in [readme.md] for examples and more explanations. fn to(self, out: &mut Dst) -> Ret { (self.fun)(out) } @@ -48,7 +53,24 @@ where /// * `Ret` - The return type of the closure /// * `Fun` - The closure type that implements `FnOnce(&mut Dst) -> Ret` /// -/// See the tutorial in [readme.me].. +/// See the tutorial in the [crate documentation](crate) for more examples and more explanations. +/// # Example +/// ``` +/// # use rosenpass_to::with_destination; +/// use crate::rosenpass_to::To; +/// let my_origin_data: [u8; 16]= [2; 16]; +/// let times_two = with_destination( move |dst: &mut [u8; 16]| { +/// for (dst, org) in dst.iter_mut().zip(my_origin_data.iter()) { +/// *dst = dst.clone() * org; +/// } +/// }); +/// let mut dst: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; +/// times_two.to(&mut dst); +/// for i in 0..16 { +/// assert_eq!(dst[i], (2 * i) as u8); +/// } +/// +/// ``` pub fn with_destination(fun: Fun) -> impl To where Dst: ?Sized,