Compare commits

...

4 Commits

Author SHA1 Message Date
Karolin Varner
caf91c84f0 chore: Remove unused warning in api integration test 2024-12-18 13:45:38 +01:00
Karolin Varner
f5b4c17011 fix: Disable asserts that rely on timing characteristics during coverage testing 2024-12-18 13:45:38 +01:00
Karolin Varner
12506e5f95 chore: Final improvements on the to crate API doc 2024-12-18 13:45:38 +01:00
David Niehues
965600212d docs+doctest(to): Add tests, examples and documentation to the to-crate 2024-12-18 13:45:38 +01:00
14 changed files with 411 additions and 51 deletions

View File

@@ -20,3 +20,6 @@ memsec = { workspace = true }
[dev-dependencies]
rand = "0.8.5"
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -113,9 +113,10 @@ mod tests {
// Pearson correlation
let correlation = cv / (sd_x * sd_y);
println!("correlation: {:.6?}", correlation);
#[cfg(not(coverage))]
assert!(
correlation.abs() < 0.01,
"execution time correlates with result"
)
);
}
}

View File

@@ -91,3 +91,6 @@ experiment_api = [
internal_signal_handling_for_coverage_reports = ["signal-hook"]
internal_testing = []
internal_bin_gen_ipc_msg_types = ["hex", "heck"]
[lints.rust]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)'] }

View File

@@ -39,7 +39,7 @@ impl Drop for KillChild {
// system is a bit broken; there is probably a few functions that just restart on EINTR
// so the signal is absorbed
loop {
rustix::process::kill_process(pid, Term).discard_result();
kill_process(pid, Term).discard_result();
if self.0.try_wait().unwrap().is_some() {
break;
}

View File

@@ -2,11 +2,9 @@
use std::{
borrow::{Borrow, BorrowMut},
collections::VecDeque,
fmt::{Debug, Write},
ops::{DerefMut, RangeBounds},
ops::DerefMut,
};
use rand::distributions::uniform::SampleBorrow;
use rosenpass_cipher_traits::Kem;
use rosenpass_ciphers::kem::StaticKem;
use rosenpass_util::result::OkExt;
@@ -28,48 +26,53 @@ fn test_successful_exchange_with_poll() -> anyhow::Result<()> {
sim.poll_loop(150)?; // Poll 75 times
let transcript = sim.transcript;
let completions: Vec<_> = transcript
let _completions: Vec<_> = transcript
.iter()
.filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_))))
.collect();
#[cfg(not(coverage))]
assert!(
!completions.is_empty(),
!_completions.is_empty(),
"\
Should have performed a successful key exchanged!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
completions[0].0 < 20.0,
_completions[0].0 < 20.0,
"\
First key exchange should happen in under twenty seconds!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
completions.len() >= 3,
_completions.len() >= 3,
"\
Should have at least two renegotiations!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
(110.0..175.0).contains(&completions[1].0),
(110.0..175.0).contains(&_completions[1].0),
"\
First renegotiation should happen in between two and three minutes!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
assert!((110.0..175.0).contains(&(completions[2].0 - completions[1].0)), "\
#[cfg(not(coverage))]
assert!((110.0..175.0).contains(&(_completions[2].0 - _completions[1].0)), "\
First renegotiation should happen in between two and three minutes after the first renegotiation!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
");
Ok(())
@@ -106,48 +109,53 @@ fn test_successful_exchange_under_packet_loss() -> anyhow::Result<()> {
}
let transcript = sim.transcript;
let completions: Vec<_> = transcript
let _completions: Vec<_> = transcript
.iter()
.filter(|elm| matches!(elm, (_, TranscriptEvent::CompletedExchange(_))))
.collect();
#[cfg(not(coverage))]
assert!(
!completions.is_empty(),
!_completions.is_empty(),
"\
Should have performed a successful key exchanged!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
completions[0].0 < 10.0,
_completions[0].0 < 10.0,
"\
First key exchange should happen in under twenty seconds!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
completions.len() >= 3,
_completions.len() >= 3,
"\
Should have at least two renegotiations!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
#[cfg(not(coverage))]
assert!(
(110.0..175.0).contains(&completions[1].0),
(110.0..175.0).contains(&_completions[1].0),
"\
First renegotiation should happen in between two and three minutes!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
"
);
assert!((110.0..175.0).contains(&(completions[2].0 - completions[1].0)), "\
#[cfg(not(coverage))]
assert!((110.0..175.0).contains(&(_completions[2].0 - _completions[1].0)), "\
First renegotiation should happen in between two and three minutes after the first renegotiation!\n\
Transcript: {transcript:?}\n\
Completions: {completions:?}\
Completions: {_completions:?}\
");
Ok(())

View File

@@ -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()) {

View File

@@ -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<T>(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<T>(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<T>(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<T>(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<T>(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<T, const N: usize>(origin: &[T; N]) -> impl To<[T; N], ()> + '_
where
T: Copy,

View File

@@ -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<Val, Ret>(pub Val, pub Ret);
@@ -59,7 +64,7 @@ impl<Val, Ret> Beside<Val, Ret> {
&mut self.1
}
/// Perform beside condensation. See [CondenseBeside]
/// Perform beside condensation. See [CondenseBeside] for more details.
///
/// # Example
/// ```
@@ -90,3 +95,25 @@ impl<Val, Ret> From<Beside<Val, Ret>> for (Val, Ret) {
(val, ret)
}
}
#[cfg(test)]
mod tests {
use crate::Beside;
#[test]
fn from_tuple() {
let tuple = (21u8, 42u16);
let beside: Beside<u8, u16> = Beside::from(tuple);
assert_eq!(beside.dest(), &21u8);
assert_eq!(beside.ret(), &42u16);
}
#[test]
fn from_beside() {
let beside: Beside<u8, u16> = Beside(21u8, 42u16);
type U8u16 = (u8, u16);
let tuple = U8u16::from(beside);
assert_eq!(tuple.0, 21u8);
assert_eq!(tuple.1, 42u16);
}
}

View File

@@ -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<Val, Error>](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<Val> CondenseBeside<Val> for Option<()> {
/// type Condensed = Option<Val>;
///
/// /// Replaces the empty tuple inside this [Option] with `ret`.
/// fn condense(self, ret: Val) -> Option<Val> {
/// self.map(|()| ret)
/// }
/// }
/// ```
pub trait CondenseBeside<Val> {
/// The type that results from condensation.
type Condensed;
@@ -17,6 +37,7 @@ pub trait CondenseBeside<Val> {
impl<Val> CondenseBeside<Val> for () {
type Condensed = Val;
/// Replaces this empty tuple with `ret`.
fn condense(self, ret: Val) -> Val {
ret
}
@@ -25,6 +46,7 @@ impl<Val> CondenseBeside<Val> for () {
impl<Val, Error> CondenseBeside<Val> for Result<(), Error> {
type Condensed = Result<Val, Error>;
/// Replaces the empty tuple inside this [Result] with `ret`.
fn condense(self, ret: Val) -> Result<Val, Error> {
self.map(|()| ret)
}
@@ -33,6 +55,7 @@ impl<Val, Error> CondenseBeside<Val> for Result<(), Error> {
impl<Val> CondenseBeside<Val> for Option<()> {
type Condensed = Option<Val>;
/// Replaces the empty tuple inside this [Option] with `ret`.
fn condense(self, ret: Val) -> Option<Val> {
self.map(|()| ret)
}

View File

@@ -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<Dst: ?Sized> {
/// Performs an explicit coercion to the destination type.
fn coerce_dest(&mut self) -> &mut Dst;

View File

@@ -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<Dst>`; (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<Dst>`; (e.g. [u8; 16]) Some owned storage that can be borrowed as `Dst`
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>) The combiation of Val and Ret after condensing was applied (`Beside<Val, Ret>::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<Dst>`; (e.g. [u8; 16]) Some owned storage that can be borrowed as
//! `Dst`
//! - `Condensed: Sized = CondenseBeside<Val>::Condensed`; (e.g. [u8; 16], Result<[u8; 16]>)
//! The combiation of Val and Ret after condensing was applied
//! (`Beside<Val, Ret>::condense()`/`Ret::condense(v)` for all `v : Val`).
pub mod beside;
pub mod condense;

View File

@@ -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<Coercable, Src, Dst, Ret>(dst: &mut Coercable, src: Src) -> Ret
where
Coercable: ?Sized + DstCoercion<Dst>,

View File

@@ -1,12 +1,46 @@
//! Module that contains the [To] crate which is the container used to
//! implement the core functionality of this crate.
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<Destination,
/// Return_value`.
/// an object that is an instance of this trait, or they will return `-> impl To<Destination,
/// Return_value>`.
///
/// 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<Dst: ?Sized, Ret>: Sized {
/// Writes self to the destination `out` and returns a value of type `Ret`.
///
@@ -19,6 +53,36 @@ pub trait To<Dst: ?Sized, Ret>: 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<Val, Fun>(self, fun: Fun) -> Beside<Val, Ret>
where
Val: BorrowMut<Dst>,
@@ -31,10 +95,21 @@ pub trait To<Dst: ?Sized, Ret>: 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<Dst, Ret>
where
Dst: Sized + Default,
@@ -53,6 +128,19 @@ pub trait To<Dst: ?Sized, Ret>: 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<Val>(self) -> Beside<Val, Ret>
where
Val: Default + BorrowMut<Dst>,
@@ -64,6 +152,36 @@ pub trait To<Dst: ?Sized, Ret>: 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<Val, Fun>(self, fun: Fun) -> <Ret as CondenseBeside<Val>>::Condensed
where
Ret: CondenseBeside<Val>,
@@ -77,6 +195,18 @@ pub trait To<Dst: ?Sized, Ret>: 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) -> <Ret as CondenseBeside<Dst>>::Condensed
where
Dst: Sized + Default,
@@ -89,6 +219,19 @@ pub trait To<Dst: ?Sized, Ret>: 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<Val>(self) -> <Ret as CondenseBeside<Val>>::Condensed
where
Val: Default + BorrowMut<Dst>,

View File

@@ -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<Dst, Ret, Fun>
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<Box<Dst>>,
}
/// 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<Dst, Ret, Fun> To<Dst, Ret> for ToClosure<Dst, Ret, Fun>
@@ -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<Dst, Ret, Fun>(fun: Fun) -> impl To<Dst, Ret>
where
Dst: ?Sized,