use std::ops::Bound;
use std::sync::LazyLock;
use arbitrary::Arbitrary;
use surrealdb_types::ToSql as _;
use crate::sql::access_type::{JwtAccess, JwtAccessIssue, JwtAccessVerify};
use crate::sql::arbitrary::idiom::plain_idiom;
use crate::sql::arbitrary::{arb_vec1, arb_vec2, atleast_one, basic_idiom};
use crate::sql::field::Selector;
use crate::sql::order::{OrderList, Ordering};
use crate::sql::statements::access::Subject;
use crate::sql::statements::define::config::api::Middleware;
use crate::sql::{
Closure, Data, Expr, Fetch, Field, Fields, Function, FunctionCall, Group, Groups, Idiom, Kind,
Literal, Lookup, Model, Order, Part, RecordIdKeyLit, RecordIdKeyRangeLit, RecordIdLit, Scoring,
Split, Splits,
};
use crate::syn::parser::{PATHS, PathKind};
static FN_PATH_INDECIES: LazyLock<Vec<usize>> = LazyLock::new(|| {
PATHS
.values()
.enumerate()
.filter(|(_, (k, _))| matches!(k, PathKind::Function))
.map(|x| x.0)
.collect()
});
impl<'a> Arbitrary<'a> for Function {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
match u.int_in_range::<u8>(0..=5)? {
0 => {
let pick = u.int_in_range(0..=(FN_PATH_INDECIES.len() - 1))?;
let idx = FN_PATH_INDECIES[pick];
Ok(Function::Normal(PATHS.keys().nth(idx).expect("should be in range").to_string()))
}
1 => Ok(Self::Custom(u.arbitrary()?)),
2 => Ok(Self::Script(u.arbitrary()?)),
3 => Ok(Self::Model(u.arbitrary()?)),
4 => Ok(Self::Module(u.arbitrary()?, u.arbitrary()?)),
5 => Ok(Self::Silo {
org: u.arbitrary()?,
pkg: u.arbitrary()?,
major: u.arbitrary()?,
minor: u.arbitrary()?,
patch: u.arbitrary()?,
sub: u.arbitrary()?,
}),
_ => unreachable!(),
}
}
}
impl<'a> Arbitrary<'a> for Closure {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let args = u.arbitrary()?;
let returns: Option<Kind> = u.arbitrary()?;
let body = if returns.is_some() {
Expr::Block(u.arbitrary()?)
} else {
u.arbitrary()?
};
Ok(Closure {
args,
returns,
body,
})
}
}
pub fn data_values<'a>(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<Vec<Vec<(Idiom, Expr)>>> {
let mut idioms: Vec<Idiom> = vec![plain_idiom(u)?];
for _ in 0..u.arbitrary_len::<Idiom>()? {
idioms.push(plain_idiom(u)?);
}
let mut values = Vec::with_capacity(idioms.len());
for i in idioms.iter() {
values.push((i.clone(), u.arbitrary()?));
}
let mut res = vec![values];
res.reserve_exact(u.arbitrary_len::<Vec<Expr>>()?);
for _ in 1..res.capacity() {
let mut values = Vec::with_capacity(idioms.len());
for i in idioms.iter() {
values.push((i.clone(), u.arbitrary()?));
}
res.push(values)
}
Ok(res)
}
impl<'a> Arbitrary<'a> for Data {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let r = match u.int_in_range(0u8..=5)? {
0 => Data::SetExpression(atleast_one(u)?),
1 => Data::UnsetExpression(arb_vec1(u, |u| plain_idiom(u))?),
2 => Data::PatchExpression(u.arbitrary()?),
3 => Data::MergeExpression(u.arbitrary()?),
4 => Data::ReplaceExpression(u.arbitrary()?),
5 => Data::ContentExpression(u.arbitrary()?),
_ => unreachable!(),
};
Ok(r)
}
}
pub fn insert_data<'a>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Data> {
let res = match u.int_in_range::<u8>(0..=1)? {
0 => Data::SingleExpression(u.arbitrary()?),
1 => {
let res = data_values(u)?;
Data::ValuesExpression(res)
}
_ => unreachable!(),
};
Ok(res)
}
impl<'a> Arbitrary<'a> for Model {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let version = format!(
"{}.{}.{}",
u.arbitrary::<u32>()?,
u.arbitrary::<u32>()?,
u.arbitrary::<u32>()?
);
Ok(Model {
version: version.into(),
name: u.arbitrary()?,
})
}
}
impl<'a> Arbitrary<'a> for Fetch {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let r = match u.int_in_range(0u8..=2)? {
0 => Expr::Param(u.arbitrary()?),
1 => Expr::FunctionCall(Box::new(FunctionCall {
receiver: Function::Normal("type::field".to_string()),
arguments: u.arbitrary()?,
})),
2 => Expr::Idiom(plain_idiom(u)?),
_ => unreachable!(),
};
Ok(Fetch(r))
}
}
impl<'a> Arbitrary<'a> for RecordIdKeyRangeLit {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let start = match u.int_in_range(0u8..=2)? {
0 => Bound::Unbounded,
1 => Bound::Included(record_id_key_no_range(u)?),
2 => Bound::Excluded(record_id_key_no_range(u)?),
_ => unreachable!(),
};
let end = match u.int_in_range(0u8..=2)? {
0 => Bound::Unbounded,
1 => Bound::Included(record_id_key_no_range(u)?),
2 => Bound::Excluded(record_id_key_no_range(u)?),
_ => unreachable!(),
};
Ok(RecordIdKeyRangeLit {
start,
end,
})
}
}
pub fn record_id_no_range<'a>(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<RecordIdLit> {
Ok(RecordIdLit {
table: u.arbitrary()?,
key: record_id_key_no_range(u)?,
})
}
pub fn record_id_key_no_range<'a>(
u: &mut arbitrary::Unstructured<'a>,
) -> arbitrary::Result<RecordIdKeyLit> {
let r = match u.int_in_range(0u8..=5)? {
0 => RecordIdKeyLit::Number(u.arbitrary()?),
1 => RecordIdKeyLit::String(u.arbitrary()?),
2 => RecordIdKeyLit::Uuid(u.arbitrary()?),
3 => RecordIdKeyLit::Array(u.arbitrary()?),
4 => RecordIdKeyLit::Object(u.arbitrary()?),
5 => RecordIdKeyLit::Generate(u.arbitrary()?),
_ => unreachable!(),
};
Ok(r)
}
impl<'a> Arbitrary<'a> for Subject {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let r = match u.int_in_range(0u8..=1)? {
0 => Subject::User(u.arbitrary()?),
1 => Subject::Record(record_id_no_range(u)?),
_ => unreachable!(),
};
Ok(r)
}
}
impl<'a> Arbitrary<'a> for Selector {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let alias = if u.arbitrary()? {
Some(basic_idiom(u)?)
} else {
None
};
let expr = match u.arbitrary()? {
Expr::Table(x) => Expr::Idiom(Idiom::field(x)),
x => x,
};
Ok(Selector {
expr,
alias,
})
}
}
impl<'a> Arbitrary<'a> for Lookup {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let r = match u.int_in_range(0u8..=2)? {
0 => Lookup {
kind: u.arbitrary()?,
..Default::default()
},
1 => Lookup {
kind: u.arbitrary()?,
what: vec![u.arbitrary()?],
..Default::default()
},
2 => {
let mut expr: Option<Fields> = u.arbitrary()?;
let (split, group, order) = if let Some(expr) = &mut expr {
let split = if u.arbitrary()? {
Some(arb_splits(u, expr)?)
} else {
None
};
let group = if u.arbitrary()? {
Some(arb_group(u, expr)?)
} else {
None
};
let order = if u.arbitrary()? {
Some(arb_order(u, expr)?)
} else {
None
};
(split, group, order)
} else {
(None, None, None)
};
let alias = match u.int_in_range(0u8..=1)? {
0 => Some(plain_idiom(u)?),
1 => None,
_ => unreachable!(),
};
let only = expr.is_some() && u.arbitrary()?;
Lookup {
kind: u.arbitrary()?,
expr,
only,
what: u.arbitrary()?,
cond: u.arbitrary()?,
split,
group,
order,
limit: u.arbitrary()?,
start: u.arbitrary()?,
alias,
}
}
_ => unreachable!(),
};
Ok(r)
}
}
impl<'a> Arbitrary<'a> for Middleware {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let mut name = "api::".to_string();
name.push_str(u.arbitrary()?);
Ok(Middleware {
name: name.into(),
args: u.arbitrary()?,
})
}
}
pub fn either_kind<'a>(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Vec<Kind>> {
let mut k = arb_vec2(u, |u| {
let r = match u.int_in_range(0u8..=22)? {
0 => Kind::None,
1 => Kind::Null,
2 => Kind::Bool,
3 => Kind::Bytes,
4 => Kind::Datetime,
5 => Kind::Decimal,
6 => Kind::Duration,
7 => Kind::Float,
8 => Kind::Int,
9 => Kind::Number,
10 => Kind::Object,
11 => Kind::String,
12 => Kind::Uuid,
13 => Kind::Regex,
14 => Kind::Table(u.arbitrary()?),
15 => Kind::Record(u.arbitrary()?),
16 => Kind::Geometry(u.arbitrary()?),
17 => Kind::Either(either_kind(u)?),
18 => Kind::Set(u.arbitrary()?, u.arbitrary()?),
19 => Kind::Array(u.arbitrary()?, u.arbitrary()?),
20 => Kind::Function(u.arbitrary()?, u.arbitrary()?),
21 => Kind::Range,
22 => Kind::Literal(u.arbitrary()?),
23 => Kind::File(u.arbitrary()?),
_ => unreachable!(),
};
Ok(r)
})?;
let Kind::Either(k) = Kind::either(k.clone()) else {
if k.contains(&Kind::None) {
k.push(Kind::Null)
} else {
k.push(Kind::None)
};
let Kind::Either(k) = Kind::either(k) else {
unreachable!()
};
return Ok(k);
};
Ok(k)
}
pub fn arb_splits<'a>(
u: &mut arbitrary::Unstructured<'a>,
expr: &mut Fields,
) -> arbitrary::Result<Splits> {
if expr.contains_all() {
return arb_vec1(u, |u| basic_idiom(u).map(Split)).map(Splits);
}
let mut res = vec![Split(idiom_from_expr(u, expr)?)];
res.reserve_exact(u.arbitrary_len::<Idiom>()?);
for _ in 1..=res.capacity() {
res.push(Split(idiom_from_expr(u, expr)?))
}
Ok(Splits(res))
}
pub fn arb_order<'a>(
u: &mut arbitrary::Unstructured<'a>,
expr: &mut Fields,
) -> arbitrary::Result<Ordering> {
if u.arbitrary()? {
return Ok(Ordering::Random);
}
if expr.contains_all() {
return Ok(Ordering::Order(u.arbitrary()?));
}
let mut res = vec![Order {
value: idiom_from_expr(u, expr)?,
collate: u.arbitrary()?,
numeric: u.arbitrary()?,
direction: u.arbitrary()?,
}];
res.reserve_exact(u.arbitrary_len::<Idiom>()?);
for _ in 1..=res.capacity() {
res.push(Order {
value: idiom_from_expr(u, expr)?,
collate: u.arbitrary()?,
numeric: u.arbitrary()?,
direction: u.arbitrary()?,
});
}
Ok(Ordering::Order(OrderList(res)))
}
pub fn arb_group<'a>(
u: &mut arbitrary::Unstructured<'a>,
expr: &mut Fields,
) -> arbitrary::Result<Groups> {
if expr.contains_all() {
return arb_vec1(u, |u| basic_idiom(u).map(Group)).map(Groups);
}
let mut res = vec![Group(idiom_from_expr(u, expr)?)];
res.reserve_exact(u.arbitrary_len::<Idiom>()?);
for _ in 1..=res.capacity() {
res.push(Group(idiom_from_expr(u, expr)?))
}
Ok(Groups(res))
}
fn idiom_is_basic(idiom: &Idiom) -> bool {
let Some(Part::Field(_)) = idiom.0.first() else {
return false;
};
for p in idiom.0[1..].iter() {
if !matches!(
p,
Part::Field(_)
| Part::All | Part::Value(Expr::Literal(
Literal::Integer(_) | Literal::Float(_) | Literal::Decimal(_)
))
) {
return false;
}
}
true
}
fn idiom_from_expr<'a>(
u: &mut arbitrary::Unstructured<'a>,
expr: &mut Fields,
) -> arbitrary::Result<Idiom> {
if expr.contains_all() {
return basic_idiom(u);
}
match expr {
Fields::Value(selector) => {
if let Some(alias) = selector.alias.clone() {
return Ok(alias);
}
match &selector.expr {
Expr::Idiom(x) => {
if idiom_is_basic(x) {
return Ok(x.clone());
}
let first = selector.clone();
let res = basic_idiom(u)?;
*expr = Fields::Select(vec![
Field::Single(*first),
Field::Single(Selector {
expr: Expr::Idiom(res.clone()),
alias: None,
}),
]);
Ok(res)
}
x => {
let s = x.to_sql();
let s = crate::syn::parse_with_settings(
s.as_bytes(),
crate::syn::ParserSettings {
legacy_strands: false,
flexible_record_id: true,
object_recursion_limit: 10000000,
query_recursion_limit: 1000000,
expr_recursion_limit: 1000000,
files_enabled: true,
surrealism_enabled: true,
json_string_escapes: false,
},
async |p, stk| p.parse_expr_start(stk).await,
)
.expect("");
Ok(s.to_idiom())
}
}
}
Fields::Select(fields) => {
let Field::Single(selector) = u.choose(fields)? else {
return basic_idiom(u);
};
if let Some(alias) = selector.alias.clone() {
return Ok(alias);
}
match &selector.expr {
Expr::Idiom(x) => {
if idiom_is_basic(x) {
return Ok(x.clone());
}
let idiom = basic_idiom(u)?;
fields.push(Field::Single(Selector {
expr: Expr::Idiom(idiom.clone()),
alias: None,
}));
Ok(idiom)
}
x => {
let s = x.to_sql();
let s = crate::syn::parse_with_settings(
s.as_bytes(),
crate::syn::ParserSettings {
legacy_strands: false,
flexible_record_id: true,
object_recursion_limit: 10000000,
query_recursion_limit: 1000000,
expr_recursion_limit: 1000000,
files_enabled: true,
surrealism_enabled: true,
json_string_escapes: false,
},
async |p, stk| p.parse_expr_start(stk).await,
)
.expect("");
Ok(s.to_idiom())
}
}
}
}
}
impl<'a> Arbitrary<'a> for JwtAccess {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let verify = u.arbitrary()?;
let issue = if u.arbitrary()? {
let alg = if let JwtAccessVerify::Key(ref ver) = verify {
ver.alg
} else {
u.arbitrary()?
};
if alg.is_symmetric() {
None
} else {
let key = u.arbitrary()?;
Some(JwtAccessIssue {
alg,
key,
})
}
} else {
None
};
Ok(JwtAccess {
verify,
issue,
})
}
}
impl<'a> Arbitrary<'a> for Scoring {
fn size_hint(depth: usize) -> (usize, Option<usize>) {
arbitrary::size_hint::and(f32::size_hint(depth), f32::size_hint(depth))
}
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Scoring::Bm {
k1: u.arbitrary()?,
b: u.arbitrary()?,
})
}
}