Skip to main content

afia_component/input/
mock.rs

1//! Types used to mock a component's inputs.
2
3use std::sync::{Arc, Mutex};
4
5use crate::input::{ComponentInputs, TextInputId};
6
7/// Used during tests to mock the component's inputs.
8#[derive(Clone)]
9pub struct MockComponentInputs {
10    inputs: Arc<Mutex<MockComponentInputsInner>>,
11}
12struct MockComponentInputsInner {
13    text: Box<dyn FnMut(TextInputId) -> String>,
14}
15
16impl MockComponentInputs {
17    /// Create a new `MockComponentInputs`.
18    pub fn new() -> Self {
19        Self {
20            inputs: Arc::new(Mutex::new(MockComponentInputsInner {
21                text: Box::new(|_| panic!("Use `MockComponentInputs::set_text_input`")),
22            })),
23        }
24    }
25
26    /// Set the function that return a text input.
27    pub fn set_text_input(&self, text_input: impl FnMut(TextInputId) -> String + 'static) {
28        self.inputs.lock().unwrap().text = Box::new(text_input);
29    }
30
31    /// Create a [`ComponentInputs`] that uses this `MockComponentInputs`.
32    pub fn component_inputs(&self) -> ComponentInputs {
33        let inputs = self.clone();
34        let inputs = Box::into_raw(Box::new(inputs));
35
36        ComponentInputs {
37            ctx: inputs.cast(),
38            free_ctx: |inputs| {
39                let inputs: *mut MockComponentInputs = inputs.cast();
40                let inputs: Box<MockComponentInputs> = unsafe { Box::from_raw(inputs) };
41                drop(inputs)
42            },
43            text: |inputs, input_id| {
44                let inputs: *mut MockComponentInputs = inputs.cast();
45                let inputs = unsafe { &*inputs };
46
47                let mut inputs = inputs.inputs.lock().unwrap();
48
49                (inputs.text)(input_id)
50            },
51        }
52    }
53}