use crate::cbor_utils::{as_bool, as_u64, map_get};
use crate::native::core::{ManyState, NativeTestCase, StopTest};
use ciborium::Value;
use super::{cbor_to_i128, interpret_schema, many_more, many_reject};
pub(super) fn interpret_tuple(ntc: &mut NativeTestCase, schema: &Value) -> Result<Value, StopTest> {
let elements = match map_get(schema, "elements") {
Some(Value::Array(arr)) => arr,
_ => panic!("tuple schema must have elements array"),
};
let mut results = Vec::with_capacity(elements.len());
for element_schema in elements {
results.push(interpret_schema(ntc, element_schema)?);
}
Ok(Value::Array(results))
}
pub(super) fn interpret_one_of(
ntc: &mut NativeTestCase,
schema: &Value,
) -> Result<Value, StopTest> {
let generators = match map_get(schema, "generators") {
Some(Value::Array(arr)) => arr,
_ => panic!("one_of schema must have generators array"),
};
assert!(
!generators.is_empty(),
"one_of schema must have at least one generator"
);
let idx = ntc.draw_integer(0, generators.len() as i128 - 1)?;
let value = interpret_schema(ntc, &generators[idx as usize])?;
Ok(Value::Array(vec![
Value::Integer((idx as i64).into()),
value,
]))
}
pub(super) fn interpret_sampled_from(
ntc: &mut NativeTestCase,
schema: &Value,
) -> Result<Value, StopTest> {
let values = match map_get(schema, "values") {
Some(Value::Array(arr)) => arr,
_ => panic!("sampled_from schema must have values array"),
};
assert!(
!values.is_empty(),
"sampled_from schema must have at least one value"
);
let idx = ntc.draw_integer(0, values.len() as i128 - 1)?;
Ok(encode_schema_value(&values[idx as usize]))
}
pub(super) fn interpret_list(ntc: &mut NativeTestCase, schema: &Value) -> Result<Value, StopTest> {
let element_schema = map_get(schema, "elements").expect("list schema must have elements");
let min_size = map_get(schema, "min_size").and_then(as_u64).unwrap_or(0) as usize;
let max_size = map_get(schema, "max_size")
.and_then(as_u64)
.map(|n| n as usize);
let unique = map_get(schema, "unique").and_then(as_bool).unwrap_or(false);
if unique {
if let Some((min_val, max_val)) = bounded_integer_range(element_schema) {
let range_size = (max_val - min_val + 1) as usize;
return interpret_unique_integer_list(ntc, min_size, max_size, min_val, range_size);
}
}
let mut state = ManyState::new(min_size, max_size);
let mut results: Vec<Value> = Vec::new();
loop {
if !many_more(ntc, &mut state)? {
break;
}
let element = interpret_schema(ntc, element_schema)?;
if unique && results.iter().any(|existing| existing == &element) {
many_reject(ntc, &mut state)?;
continue;
}
results.push(element);
}
Ok(Value::Array(results))
}
fn interpret_unique_integer_list(
ntc: &mut NativeTestCase,
min_size: usize,
max_size: Option<usize>,
min_val: i128,
range_size: usize,
) -> Result<Value, StopTest> {
let effective_max = max_size.map_or(range_size, |m| m.min(range_size));
let mut state = ManyState::new(min_size, Some(effective_max));
let mut remaining: Vec<i128> = (min_val..min_val + range_size as i128).collect();
let mut results = Vec::new();
loop {
if remaining.is_empty() || !many_more(ntc, &mut state)? {
break;
}
let j = ntc.draw_integer(0, remaining.len() as i128 - 1)? as usize;
let value = remaining.remove(j);
results.push(Value::Integer((value as i64).into()));
}
Ok(Value::Array(results))
}
fn bounded_integer_range(schema: &Value) -> Option<(i128, i128)> {
use crate::cbor_utils::as_text;
let schema_type = map_get(schema, "type").and_then(as_text)?;
if schema_type != "integer" {
return None;
}
let min_val = cbor_to_i128(map_get(schema, "min_value")?);
let max_val = cbor_to_i128(map_get(schema, "max_value")?);
if !(1..=10_000).contains(&(max_val - min_val + 1)) {
return None;
}
Some((min_val, max_val))
}
pub(super) fn interpret_dict(ntc: &mut NativeTestCase, schema: &Value) -> Result<Value, StopTest> {
let key_schema = map_get(schema, "keys").expect("dict schema must have keys");
let val_schema = map_get(schema, "values").expect("dict schema must have values");
let min_size = map_get(schema, "min_size").and_then(as_u64).unwrap_or(0) as usize;
let max_size = map_get(schema, "max_size")
.and_then(as_u64)
.map(|n| n as usize);
let mut state = ManyState::new(min_size, max_size);
let mut pairs: Vec<Value> = Vec::new();
let mut keys: Vec<Value> = Vec::new();
loop {
if !many_more(ntc, &mut state)? {
break;
}
let key = interpret_schema(ntc, key_schema)?;
if keys.iter().any(|existing| existing == &key) {
many_reject(ntc, &mut state)?;
continue;
}
let value = interpret_schema(ntc, val_schema)?;
keys.push(key.clone());
pairs.push(Value::Array(vec![key, value]));
}
Ok(Value::Array(pairs))
}
fn encode_schema_value(value: &Value) -> Value {
match value {
Value::Text(s) => Value::Tag(91, Box::new(Value::Bytes(s.as_bytes().to_vec()))),
other => other.clone(),
}
}
#[cfg(test)]
#[path = "../../../tests/embedded/native/schema/collections_tests.rs"]
mod tests;