#![allow(clippy::borrow_interior_mutable_const)]
#[allow(clippy::declare_interior_mutable_const)]
#[allow(clippy::borrowed_box)]
mod mock;
pub use crate::mock::MockFn;
use crate::mock::{
MockAddressFn, MockBoolFn, MockColorFn, MockDateFn, MockDateTimeFn, MockDomainFn, MockEmailFn,
MockFirstNameFn, MockFloatFn, MockFns, MockGuidFn, MockHSLFn, MockIdFn, MockIdNumberFn,
MockImageFn, MockIpFn, MockLastNameFn, MockNameFn, MockNumberFn, MockParagraphFn, MockPhoneFn,
MockRGBAFn, MockRGBFn, MockSentenceFn, MockTimeFn, MockTimestampFn, MockTitleFn, MockTokenFn,
MockUrlFn, MockZipFn,
};
use once_cell::sync::Lazy;
use rand::Rng;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Mutex;
static MOCK_FNS: Lazy<Mutex<MockFns>> = Lazy::new(|| {
let mut mock_fns = MockFns::default();
mock_fns.registry("@Guid", MockGuidFn::default());
mock_fns.registry("@Id", MockIdFn::default());
mock_fns.registry("@IdNumber", MockIdNumberFn::default());
mock_fns.registry("@Phone", MockPhoneFn::default());
mock_fns.registry("@Bool", MockBoolFn::default());
mock_fns.registry("@Name", MockNameFn::default());
mock_fns.registry("@FirstName", MockFirstNameFn::default());
mock_fns.registry("@LastName", MockLastNameFn::default());
mock_fns.registry("@Token", MockTokenFn::default());
mock_fns.registry("@Url", MockUrlFn::default());
mock_fns.registry("@Email", MockEmailFn::default());
mock_fns.registry("@Ip", MockIpFn::default());
mock_fns.registry("@Domain", MockDomainFn::default());
mock_fns.registry("@Image", MockImageFn::default());
mock_fns.registry("@Number", MockNumberFn::default());
mock_fns.registry("@Float", MockFloatFn::default());
mock_fns.registry("@Zip", MockZipFn::default());
mock_fns.registry("@Address", MockAddressFn::default());
mock_fns.registry("@Paragraph", MockParagraphFn::default());
mock_fns.registry("@Sentence", MockSentenceFn::default());
mock_fns.registry("@Title", MockTitleFn::default());
mock_fns.registry("@Date", MockDateFn::default());
mock_fns.registry("@Time", MockTimeFn::default());
mock_fns.registry("@DateTime", MockDateTimeFn::default());
mock_fns.registry("@Timestamp", MockTimestampFn::default());
mock_fns.registry("@Color", MockColorFn::default());
mock_fns.registry("@RGB", MockRGBFn::default());
mock_fns.registry("@RGBA", MockRGBAFn::default());
mock_fns.registry("@HSL", MockHSLFn::default());
Mutex::new(mock_fns)
});
pub fn registry(mock_name: &'static str, mock_fn: impl MockFn + 'static) {
let mut mock_fns = MOCK_FNS.lock().unwrap();
mock_fns.registry(mock_name, mock_fn);
}
pub fn mock(val: &Value) -> Value {
match val {
Value::String(str) => {
if str.starts_with('@') {
let fns = MOCK_FNS.lock().unwrap();
let mut fields = str.split('|').collect::<Vec<&str>>();
let mock_name = fields.remove(0);
if let Some(mock_fn) = fns.get_mock_fn(mock_name) {
return if fields.is_empty() {
mock_fn.mock(None)
} else {
mock_fn.mock(Some(fields))
};
}
val.to_owned()
} else {
val.to_owned()
}
}
Value::Object(map) => {
let mut val_map = HashMap::new();
for (key, val) in map {
val_map.insert(key, mock(val));
}
json!(val_map)
}
Value::Array(arr) => {
return if arr.len() == 3 {
let value = arr.get(0);
let min = arr.get(1).and_then(|v| v.as_u64());
let max = arr.get(2).and_then(|v| v.as_u64());
match (value, min, max) {
(Some(value), Some(min), Some(max)) => {
let mut vec = vec![];
let mut rng = rand::thread_rng();
let len = rng.gen_range(min..max);
for _ in 0..len {
vec.push(mock(value));
}
json!(vec)
}
_ => val.to_owned(),
}
} else {
val.to_owned()
};
}
_ => val.to_owned(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn it_works() {
dbg!(mock(&json!("@Float")));
dbg!(mock(&json!("@Float|3")));
dbg!(mock(&json!("@Float|-100~100")));
dbg!(mock(&json!("@Float|3|-100~100")));
let val = mock(&json!({
"code":0,
"msg":"just text",
"data":[{
"id":"@Id|10",
"title": "@Title",
"datetime":"@DateTime",
"author":{
"name":"@Name",
"id":"@Guid",
"email":"@Email",
"id_number":"@IdNumber",
"ip":"@Ip",
"phones":["@Phone", 1, 3],
"blog":"@Url",
"avatar":"@Image|80X80|f7f7f7|fff"
}
}, 1, 3]
}));
assert!(!val.to_string().is_empty());
}
}