1use std::collections::HashMap;
9
10use lua_types::{LuaError as VmError, LuaType};
11use lua_vm::api;
12use lua_vm::state::LuaState;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct AnyLuaString(pub Vec<u8>);
17
18#[derive(Clone, Debug, PartialEq)]
20pub enum AnyLuaValue {
21 LuaString(String),
22 LuaAnyString(AnyLuaString),
23 LuaNumber(f64),
24 LuaBoolean(bool),
25 LuaArray(Vec<(AnyLuaValue, AnyLuaValue)>),
26 LuaNil,
27 LuaOther,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
33pub enum AnyHashableLuaValue {
34 LuaString(String),
35 LuaAnyString(AnyLuaString),
36 LuaNumber(i32),
37 LuaBoolean(bool),
38 LuaArray(Vec<(AnyHashableLuaValue, AnyHashableLuaValue)>),
39 LuaNil,
40 LuaOther,
41}
42
43pub(crate) fn push_any(state: &mut LuaState, value: &AnyLuaValue) -> Result<(), VmError> {
45 match value {
46 AnyLuaValue::LuaNil | AnyLuaValue::LuaOther => api::push_nil(state),
47 AnyLuaValue::LuaBoolean(b) => api::push_boolean(state, *b),
48 AnyLuaValue::LuaNumber(n) => api::push_number(state, *n),
49 AnyLuaValue::LuaString(s) => {
50 api::push_lstring(state, s.as_bytes())?;
51 }
52 AnyLuaValue::LuaAnyString(s) => {
53 api::push_lstring(state, &s.0)?;
54 }
55 AnyLuaValue::LuaArray(pairs) => {
56 state.create_table(0, pairs.len() as i32)?;
57 let table = api::get_top(state);
58 for (k, v) in pairs {
59 push_any(state, k)?;
60 push_any(state, v)?;
61 api::raw_set(state, table)?;
62 }
63 }
64 }
65 Ok(())
66}
67
68pub(crate) fn push_hashable(
70 state: &mut LuaState,
71 value: &AnyHashableLuaValue,
72) -> Result<(), VmError> {
73 match value {
74 AnyHashableLuaValue::LuaNil | AnyHashableLuaValue::LuaOther => api::push_nil(state),
75 AnyHashableLuaValue::LuaBoolean(b) => api::push_boolean(state, *b),
76 AnyHashableLuaValue::LuaNumber(n) => api::push_integer(state, *n as i64),
77 AnyHashableLuaValue::LuaString(s) => {
78 api::push_lstring(state, s.as_bytes())?;
79 }
80 AnyHashableLuaValue::LuaAnyString(s) => {
81 api::push_lstring(state, &s.0)?;
82 }
83 AnyHashableLuaValue::LuaArray(pairs) => {
84 state.create_table(0, pairs.len() as i32)?;
85 let table = api::get_top(state);
86 for (k, v) in pairs {
87 push_hashable(state, k)?;
88 push_hashable(state, v)?;
89 api::raw_set(state, table)?;
90 }
91 }
92 }
93 Ok(())
94}
95
96pub(crate) fn read_any(state: &mut LuaState, idx: i32) -> AnyLuaValue {
98 match api::lua_type_at(state, idx) {
99 LuaType::None | LuaType::Nil => AnyLuaValue::LuaNil,
100 LuaType::Boolean => AnyLuaValue::LuaBoolean(api::to_boolean(state, idx)),
101 LuaType::Number => {
102 let n = api::to_number_x(state, idx).expect("Number value must convert to f64");
103 AnyLuaValue::LuaNumber(n)
104 }
105 LuaType::String => read_string_value(state, idx),
106 LuaType::Table => AnyLuaValue::LuaArray(read_table_pairs(state, idx)),
107 _ => AnyLuaValue::LuaOther,
108 }
109}
110
111pub(crate) fn read_hashable(state: &mut LuaState, idx: i32) -> AnyHashableLuaValue {
113 match read_any(state, idx) {
114 AnyLuaValue::LuaNil => AnyHashableLuaValue::LuaNil,
115 AnyLuaValue::LuaBoolean(b) => AnyHashableLuaValue::LuaBoolean(b),
116 AnyLuaValue::LuaNumber(n) => AnyHashableLuaValue::LuaNumber(n as i32),
117 AnyLuaValue::LuaString(s) => AnyHashableLuaValue::LuaString(s),
118 AnyLuaValue::LuaAnyString(s) => AnyHashableLuaValue::LuaAnyString(s),
119 AnyLuaValue::LuaArray(_) | AnyLuaValue::LuaOther => AnyHashableLuaValue::LuaOther,
120 }
121}
122
123fn read_string_value(state: &mut LuaState, idx: i32) -> AnyLuaValue {
125 let bytes = string_bytes_at(state, idx).expect("String value must have bytes");
126 match String::from_utf8(bytes) {
127 Ok(s) => AnyLuaValue::LuaString(s),
128 Err(e) => AnyLuaValue::LuaAnyString(AnyLuaString(e.into_bytes())),
129 }
130}
131
132pub(crate) fn string_bytes_at(state: &mut LuaState, idx: i32) -> Option<Vec<u8>> {
134 let s = api::to_lua_string(state, idx).ok()??;
135 Some(s.as_bytes().to_vec())
136}
137
138fn read_table_pairs(state: &mut LuaState, idx: i32) -> Vec<(AnyLuaValue, AnyLuaValue)> {
140 let table = api::abs_index(state, idx);
141 let mut pairs = Vec::new();
142 api::push_nil(state);
143 while api::next(state, table).unwrap_or(false) {
144 let key = read_any(state, -2);
145 let value = read_any(state, -1);
146 pairs.push((key, value));
147 api::set_top(state, -2).ok();
148 }
149 pairs
150}
151
152pub(crate) fn read_sequence(state: &mut LuaState, idx: i32) -> Vec<AnyLuaValue> {
154 let table = api::abs_index(state, idx);
155 let mut out = Vec::new();
156 let mut i = 1i64;
157 loop {
158 let t = match api::get_i(state, table, i) {
159 Ok(t) => t,
160 Err(_) => break,
161 };
162 if t == LuaType::Nil {
163 api::set_top(state, -2).ok();
164 break;
165 }
166 out.push(read_any(state, -1));
167 api::set_top(state, -2).ok();
168 i += 1;
169 }
170 out
171}
172
173pub(crate) fn read_map(
175 state: &mut LuaState,
176 idx: i32,
177) -> HashMap<AnyHashableLuaValue, AnyLuaValue> {
178 let table = api::abs_index(state, idx);
179 let mut map = HashMap::new();
180 api::push_nil(state);
181 while api::next(state, table).unwrap_or(false) {
182 let key = read_hashable(state, -2);
183 let value = read_any(state, -1);
184 map.insert(key, value);
185 api::set_top(state, -2).ok();
186 }
187 map
188}