nullable_utils/lib.rs
1// SPDX-FileCopyrightText: 2024 Markus Haug (Korrat)
2//
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6//! This crate provides helpers for working with Nullables as described by James Shore in [Testing without Mocks].
7//!
8//! [Testing without Mocks]: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks
9
10extern crate alloc;
11
12pub use nullable_utils_macros::*;
13
14pub use crate::output::Listener as OutputListener;
15pub use crate::output::Tracker as OutputTracker;
16
17mod output;
18
19/// `NulledResponses` helps with implementing [Configurable Responses] for Nullable types.
20///
21/// [Configurable Responses]: https://www.jamesshore.com/v2/projects/nullables/testing-without-mocks#configurable-responses
22#[derive(Clone, Debug)]
23// TODO exchange this for an implementation using boxed iterators?
24pub enum NulledResponses<T> {
25 /// Repeat a single response forever.
26 Repeating(T),
27 /// Return a sequence of responses, panicking when the sequence runs out.
28 Sequence(alloc::vec::Vec<T>),
29}
30
31impl<T> NulledResponses<T> {
32 /// Backmap can be used to convert between `NulledResponses` for different types.
33 ///
34 /// For example, a high-level infrastructure wrapper can use this to convert its own response configuration into a
35 /// response configuration for an embedded lower-level infrastructure wrapper.
36 pub fn backmap<U>(self, mut mapper: impl FnMut(T) -> U) -> NulledResponses<U> {
37 match self {
38 Self::Repeating(response) => NulledResponses::Repeating(mapper(response)),
39 Self::Sequence(responses) => {
40 NulledResponses::Sequence(responses.into_iter().map(mapper).collect())
41 }
42 }
43 }
44}
45
46impl<T> NulledResponses<T>
47where
48 T: Clone,
49{
50 /// Gets the next response.
51 ///
52 /// # Panics
53 ///
54 /// Panics if this object was configured with a finite sequence of responses and that sequence has been exhausted.
55 pub fn get(&mut self) -> T {
56 match self {
57 Self::Repeating(response) => response.clone(),
58 Self::Sequence(responses) => responses.pop().expect("not enough responses configured"),
59 }
60 }
61}
62
63impl<T, const N: usize> From<[T; N]> for NulledResponses<T> {
64 fn from(value: [T; N]) -> Self {
65 Self::from(Vec::from(value))
66 }
67}
68
69impl<T> From<&[T]> for NulledResponses<T>
70where
71 T: Clone,
72{
73 fn from(value: &[T]) -> Self {
74 Self::from(Vec::from(value))
75 }
76}
77
78impl<T> From<T> for NulledResponses<T>
79where
80 T: Clone,
81{
82 fn from(value: T) -> Self {
83 Self::Repeating(value)
84 }
85}
86
87impl<T> From<Vec<T>> for NulledResponses<T> {
88 fn from(mut value: Vec<T>) -> Self {
89 value.reverse();
90 Self::Sequence(value)
91 }
92}