use crate::id::v1::base::V1;
use once_cell::sync::Lazy;
use std::{
collections::HashMap,
sync::{LockResult, Mutex},
};
static mut MOCK_MAKE_VALUES: Lazy<Mutex<HashMap<String, Vec<String>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
static mut MOCK_IS_VALID_VALUES: Lazy<Mutex<HashMap<String, Vec<String>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
#[derive(Debug)]
pub struct Mock {
id: String,
}
impl Mock {
pub fn new() -> Self {
Mock::new_options(ulid::Ulid::new().to_string())
}
pub fn new_options(id: String) -> Self {
Mock {
id: id.to_lowercase(),
}
}
pub fn expected_make(&self, value: String) {
unsafe {
let value = value.to_lowercase();
let mut mock_make_values = MOCK_MAKE_VALUES.lock().ignore_poison();
if !mock_make_values.contains_key(&self.id) {
mock_make_values.insert(self.id.clone(), vec![]);
}
mock_make_values
.get_mut(&self.id)
.unwrap()
.push(value.clone());
let mut mock_is_valid_values = MOCK_IS_VALID_VALUES.lock().ignore_poison();
if !mock_is_valid_values.contains_key(&self.id) {
mock_is_valid_values.insert(self.id.clone(), vec![]);
}
mock_is_valid_values
.get_mut(&self.id)
.unwrap()
.push(value.clone());
}
}
}
impl V1 for Mock {
fn make(&self) -> String {
unsafe {
let mut mock_make_values = MOCK_MAKE_VALUES.lock().ignore_poison();
let values = match mock_make_values.get_mut(&self.id) {
Some(result) => result,
None => panic!("no values were found for this mock instance ({}), you did'nt initialize the mock correctly, to do so use the 'new' function as shown below: Mock::new()", self.id)
};
let value = match values.pop() {
Some(result) => result,
None => panic!("no value found for the requested mock instance ({}), you must set the value expected by the mock using the 'expected_make' function as shown below: mock_instance.expected_make(\"my_value\".to_string())", self.id)
};
value
}
}
fn is_valid(&self, value: String) -> bool {
unsafe {
let mock_is_valid_values = MOCK_IS_VALID_VALUES.lock().ignore_poison();
let values = match mock_is_valid_values.get(&self.id) {
None => return false,
Some(result) => result,
};
values.contains(&value)
}
}
}
pub trait LockResultExt {
type Guard;
fn ignore_poison(self) -> Self::Guard;
}
impl<Guard> LockResultExt for LockResult<Guard> {
type Guard = Guard;
fn ignore_poison(self) -> Guard {
self.unwrap_or_else(|e| e.into_inner())
}
}