Spaces:
Sleeping
Sleeping
File size: 3,431 Bytes
3afc977 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | //! Typed value interchange for program I/O.
//!
//! Inputs and outputs of generated Lua functions are restricted to a small,
//! JSON-serializable, deterministic value space. Anything outside it (functions,
//! userdata, non-sequence tables, NaN/Inf) is treated as non-observable and
//! causes the sample to be filtered out upstream.
use mlua::{Lua, Value as LuaValue};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "k", content = "v")]
pub enum LValue {
Nil,
Bool(bool),
Int(i64),
Num(f64),
Str(String),
List(Vec<LValue>),
}
impl LValue {
/// Convert a Rust-side value into a Lua value inside `lua`.
pub fn to_lua(&self, lua: &Lua) -> mlua::Result<LuaValue> {
Ok(match self {
LValue::Nil => LuaValue::Nil,
LValue::Bool(b) => LuaValue::Boolean(*b),
LValue::Int(i) => LuaValue::Integer(*i),
LValue::Num(n) => LuaValue::Number(*n),
LValue::Str(s) => LuaValue::String(lua.create_string(s)?),
LValue::List(items) => {
let t = lua.create_table()?;
for (idx, item) in items.iter().enumerate() {
t.set(idx as i64 + 1, item.to_lua(lua)?)?;
}
LuaValue::Table(t)
}
})
}
/// Best-effort conversion from a Lua value. Returns None when the value is
/// outside the observable space (function/userdata/thread, non-sequence
/// table, or a non-finite number).
pub fn from_lua(v: &LuaValue) -> Option<LValue> {
match v {
LuaValue::Nil => Some(LValue::Nil),
LuaValue::Boolean(b) => Some(LValue::Bool(*b)),
LuaValue::Integer(i) => Some(LValue::Int(*i)),
LuaValue::Number(n) => {
if n.is_finite() {
Some(LValue::Num(*n))
} else {
None
}
}
LuaValue::String(s) => {
let st = s.to_str().ok()?;
// Cap length so a runaway string.rep is treated as non-observable
// rather than ballooning the dataset.
if st.len() > 4096 {
return None;
}
Some(LValue::Str(st.to_owned()))
}
LuaValue::Table(t) => {
// Only accept array-like (sequence) tables: keys 1..=n, no holes,
// no extra keys. Bounded length to avoid pathological outputs.
let len = t.raw_len();
if len > 256 {
return None;
}
let mut items = Vec::with_capacity(len);
for i in 1..=len {
let elem: LuaValue = t.raw_get(i as i64).ok()?;
items.push(LValue::from_lua(&elem)?);
}
// Reject tables with non-sequence keys (e.g. string keys, holes).
let mut count = 0usize;
for pair in t.clone().pairs::<LuaValue, LuaValue>() {
let (_k, _v) = pair.ok()?;
count += 1;
}
if count != len {
return None;
}
Some(LValue::List(items))
}
_ => None, // function, userdata, thread, lightuserdata, error
}
}
}
|