use crate::registry::{mfa, FResult, FunctionError, Registry, TremorFn, TremorFnWrapper};
use crate::tremor_fn;
use crate::EventContext;
use rand::distributions::Alphanumeric;
use rand::{rngs::SmallRng, Rng, SeedableRng};
use simd_json::prelude::*;
use simd_json::BorrowedValue as Value;
#[derive(Clone, Debug, Default)]
struct RandomInteger {}
impl TremorFn for RandomInteger {
fn invoke<'event, 'c>(
&self,
ctx: &'c EventContext,
args: &[&Value<'event>],
) -> FResult<Value<'event>> {
let this_mfa = || mfa("random", "integer", args.len());
let mut rng = SmallRng::seed_from_u64(ctx.ingest_ns());
match args.len() {
2 => {
let (low, high) = (&args[0], &args[1]);
if let (Some(low), Some(high)) = (low.as_i64(), high.as_i64()) {
if low < high {
Ok(Value::from(rng.gen_range(low, high)))
} else {
Err(FunctionError::RuntimeError {
mfa: this_mfa(),
error:
"Invalid arguments. First argument must be lower than second argument".to_string(),
})
}
} else {
Err(FunctionError::BadType { mfa: this_mfa() })
}
}
1 => {
let input = &args[0];
if let Some(input) = input.as_u64() {
Ok(Value::from(rng.gen_range(0, input)))
} else {
Err(FunctionError::BadType { mfa: this_mfa() })
}
}
0 => Ok(Value::from(
rng.gen::<i64>(), )),
_ => Err(FunctionError::BadArity {
mfa: this_mfa(),
calling_a: args.len(),
}),
}
}
fn boxed_clone(&self) -> Box<dyn TremorFn> {
Box::new(self.clone())
}
fn arity(&self) -> std::ops::RangeInclusive<usize> {
0..=2
}
fn is_const(&self) -> bool {
false
}
}
#[derive(Clone, Debug, Default)]
struct RandomFloat {}
impl TremorFn for RandomFloat {
fn invoke<'event, 'c>(
&self,
ctx: &'c EventContext,
args: &[&Value<'event>],
) -> FResult<Value<'event>> {
let this_mfa = || mfa("random", "float", args.len());
let mut rng = SmallRng::seed_from_u64(ctx.ingest_ns());
match args.len() {
2 => {
let (low, high) = (&args[0], &args[1]);
if let (Some(low), Some(high)) = (low.cast_f64(), high.cast_f64()) {
if low < high {
Ok(Value::from(rng.gen_range(low, high)))
} else {
Err(FunctionError::RuntimeError {
mfa: this_mfa(),
error:
"Invalid arguments. First argument must be lower than second argument".to_string(),
})
}
} else {
Err(FunctionError::BadType { mfa: this_mfa() })
}
}
1 => {
let input = &args[0];
if let Some(input) = input.cast_f64() {
Ok(Value::from(rng.gen_range(0.0, input)))
} else {
Err(FunctionError::BadType { mfa: this_mfa() })
}
}
0 => Ok(Value::from(
rng.gen::<f64>(), )),
_ => Err(FunctionError::BadArity {
mfa: this_mfa(),
calling_a: args.len(),
}),
}
}
fn boxed_clone(&self) -> Box<dyn TremorFn> {
Box::new(self.clone())
}
fn arity(&self) -> std::ops::RangeInclusive<usize> {
0..=2
}
fn is_const(&self) -> bool {
false
}
}
pub fn load(registry: &mut Registry) {
registry
.insert(tremor_fn! (random::bool(_context) {
Ok(Value::from(
SmallRng::seed_from_u64(_context.ingest_ns())
.gen::<bool>()
))
}))
.insert(tremor_fn! (random::string(_context, _length) {
if let Some(n) = _length.as_usize() {
Ok(Value::String(
SmallRng::seed_from_u64(_context.ingest_ns())
.sample_iter(&Alphanumeric).take(n).collect()
))
} else {
Err(FunctionError::BadType{mfa: this_mfa()})
}
}))
.insert(TremorFnWrapper::new(
"random".to_string(),
"integer".to_string(),
Box::new(RandomInteger::default()),
))
.insert(TremorFnWrapper::new(
"random".to_string(),
"float".to_string(),
Box::new(RandomFloat::default()),
));
}
#[cfg(test)]
mod test {
use crate::registry::fun;
use simd_json::{BorrowedValue as Value, Value as ValueTrait};
#[test]
fn bool() {
let f = fun("random", "bool");
assert!(f(&[]).ok().map(|v| v.is_bool()).unwrap_or_default());
}
#[test]
fn string() {
let f = fun("random", "string");
let n = 0;
assert_val!(f(&[&Value::from(n)]), "");
let n = 16;
assert!(match f(&[&Value::from(n)]) {
Ok(Value::String(s)) => s.len() as i64 == n,
_ => false,
});
}
#[test]
fn integer() {
let f = fun("random", "integer");
let v1 = Value::from(0);
let v2 = Value::from(1);
assert_val!(f(&[&v1, &v2]), 0);
let v1 = Value::from(-42);
let v2 = Value::from(-41);
assert_val!(f(&[&v1, &v2]), -42);
let v = Value::from(1);
assert_val!(f(&[&v]), 0);
assert!(f(&[]).ok().map(|v| v.is_i64()).unwrap_or_default());
}
#[test]
fn float() {
let f = fun("random", "float");
let v1 = 0.0;
let v2 = 100.0;
assert!(f(&[&Value::from(v1), &Value::from(v2)])
.ok()
.and_then(|v| v.as_f64())
.map(|a| a >= v1 && a < v2)
.unwrap_or_default());
let v = 100.0;
assert!(f(&[&Value::from(v)])
.ok()
.and_then(|v| v.as_f64())
.map(|a| a >= 0.0 && a < v)
.unwrap_or_default());
assert!(f(&[])
.ok()
.and_then(|v| v.as_f64())
.map(|a| a >= 0.0 && a < 1.0)
.unwrap_or_default());
}
}