use core::{fmt, iter, mem};
pub struct Answers<V, Ctx = ()> {
inner: Box<dyn FnMut(&Ctx) -> V + Send>,
calls: Vec<Ctx>,
}
impl<V, Ctx: fmt::Debug> fmt::Debug for Answers<V, Ctx> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("Answers")
.field("calls", &self.calls)
.finish()
}
}
impl<V, Ctx> Default for Answers<V, Ctx> {
fn default() -> Self {
Self::from_fn(|_| panic!("no answers provided"))
}
}
impl<V, Ctx> Answers<V, Ctx> {
pub fn from_fn<F>(function: F) -> Self
where
F: FnMut(&Ctx) -> V + Send + 'static,
{
Self {
inner: Box::new(function),
calls: Vec::new(),
}
}
pub fn from_values<I>(iter: I) -> Self
where
I: IntoIterator<Item = V>,
I::IntoIter: Send + 'static,
{
let mut iter = iter.into_iter();
Self::from_fn(move |_| iter.next().expect("run out of mock responses"))
}
pub fn next_for(&mut self, context: Ctx) -> V {
let response = (self.inner)(&context);
self.calls.push(context);
response
}
pub fn take_calls(&mut self) -> Vec<Ctx> {
mem::take(&mut self.calls)
}
}
impl<V: Send + 'static, Ctx> Answers<V, Ctx> {
pub fn from_value_once(value: V) -> Self {
Self::from_values(iter::once(value))
}
}
impl<V: Clone + Send + 'static, Ctx> Answers<V, Ctx> {
pub fn from_value(value: V) -> Self {
Self::from_values(iter::repeat(value))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn answers_basics() {
let mut answers: Answers<i32> = Answers::from_values([1, 2, 3, 5]);
assert_eq!(answers.next_for(()), 1);
assert_eq!(answers.next_for(()), 2);
assert_eq!(answers.next_for(()), 3);
assert_eq!(answers.next_for(()), 5);
let calls = answers.take_calls();
assert_eq!(calls.len(), 4);
}
#[test]
fn answers_with_context() {
let mut answers: Answers<usize, String> = Answers::from_values(5..10);
let samples = ["test", "various", "strings"];
for (i, s) in samples.into_iter().enumerate() {
assert_eq!(answers.next_for(s.to_owned()), i + 5);
}
let calls = answers.take_calls();
assert_eq!(calls, samples);
let mut counter = 0;
let mut answers: Answers<usize, String> = Answers::from_fn(move |s: &String| {
counter += 1;
match s.as_str() {
"test" => 42,
_ if counter < 3 => s.len(),
_ => 0,
}
});
let real_answers: Vec<_> = samples
.into_iter()
.map(|s| answers.next_for(s.to_owned()))
.collect();
assert_eq!(real_answers, [42, 7, 0]);
let calls = answers.take_calls();
assert_eq!(calls, samples);
}
fn assert_static<T: 'static>(value: T) -> T {
value
}
#[test]
fn function_answers() {
type LenFn = fn(&str) -> usize;
let test_fn: LenFn = |s| usize::from(s == "test");
let fns = iter::repeat(str::len as LenFn).take(2).chain([test_fn]);
let answers: Answers<LenFn> = Answers::from_values(fns);
let mut answers = assert_static(answers);
assert_eq!(answers.next_for(())("test"), 4);
assert_eq!(answers.next_for(())("test"), 4);
assert_eq!(answers.next_for(())("test"), 1);
}
}