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(state: &mut LuaState, value: &AnyHashableLuaValue) -> Result<(), VmError> {
70 match value {
71 AnyHashableLuaValue::LuaNil | AnyHashableLuaValue::LuaOther => api::push_nil(state),
72 AnyHashableLuaValue::LuaBoolean(b) => api::push_boolean(state, *b),
73 AnyHashableLuaValue::LuaNumber(n) => api::push_integer(state, *n as i64),
74 AnyHashableLuaValue::LuaString(s) => {
75 api::push_lstring(state, s.as_bytes())?;
76 }
77 AnyHashableLuaValue::LuaAnyString(s) => {
78 api::push_lstring(state, &s.0)?;
79 }
80 AnyHashableLuaValue::LuaArray(pairs) => {
81 state.create_table(0, pairs.len() as i32)?;
82 let table = api::get_top(state);
83 for (k, v) in pairs {
84 push_hashable(state, k)?;
85 push_hashable(state, v)?;
86 api::raw_set(state, table)?;
87 }
88 }
89 }
90 Ok(())
91}
92
93pub(crate) fn read_any(state: &mut LuaState, idx: i32) -> AnyLuaValue {
95 match api::lua_type_at(state, idx) {
96 LuaType::None | LuaType::Nil => AnyLuaValue::LuaNil,
97 LuaType::Boolean => AnyLuaValue::LuaBoolean(api::to_boolean(state, idx)),
98 LuaType::Number => {
99 let n = api::to_number_x(state, idx).expect("Number value must convert to f64");
100 AnyLuaValue::LuaNumber(n)
101 }
102 LuaType::String => read_string_value(state, idx),
103 LuaType::Table => AnyLuaValue::LuaArray(read_table_pairs(state, idx)),
104 _ => AnyLuaValue::LuaOther,
105 }
106}
107
108pub(crate) fn read_hashable(state: &mut LuaState, idx: i32) -> AnyHashableLuaValue {
110 match read_any(state, idx) {
111 AnyLuaValue::LuaNil => AnyHashableLuaValue::LuaNil,
112 AnyLuaValue::LuaBoolean(b) => AnyHashableLuaValue::LuaBoolean(b),
113 AnyLuaValue::LuaNumber(n) => AnyHashableLuaValue::LuaNumber(n as i32),
114 AnyLuaValue::LuaString(s) => AnyHashableLuaValue::LuaString(s),
115 AnyLuaValue::LuaAnyString(s) => AnyHashableLuaValue::LuaAnyString(s),
116 AnyLuaValue::LuaArray(_) | AnyLuaValue::LuaOther => AnyHashableLuaValue::LuaOther,
117 }
118}
119
120fn read_string_value(state: &mut LuaState, idx: i32) -> AnyLuaValue {
122 let bytes = string_bytes_at(state, idx).expect("String value must have bytes");
123 match String::from_utf8(bytes) {
124 Ok(s) => AnyLuaValue::LuaString(s),
125 Err(e) => AnyLuaValue::LuaAnyString(AnyLuaString(e.into_bytes())),
126 }
127}
128
129pub(crate) fn string_bytes_at(state: &mut LuaState, idx: i32) -> Option<Vec<u8>> {
131 let s = api::to_lua_string(state, idx).ok()??;
132 Some(s.as_bytes().to_vec())
133}
134
135fn read_table_pairs(state: &mut LuaState, idx: i32) -> Vec<(AnyLuaValue, AnyLuaValue)> {
137 let table = api::abs_index(state, idx);
138 let mut pairs = Vec::new();
139 api::push_nil(state);
140 while api::next(state, table).unwrap_or(false) {
141 let key = read_any(state, -2);
142 let value = read_any(state, -1);
143 pairs.push((key, value));
144 api::set_top(state, -2).ok();
145 }
146 pairs
147}
148
149pub(crate) fn read_sequence(state: &mut LuaState, idx: i32) -> Vec<AnyLuaValue> {
151 let table = api::abs_index(state, idx);
152 let mut out = Vec::new();
153 let mut i = 1i64;
154 loop {
155 let t = match api::get_i(state, table, i) {
156 Ok(t) => t,
157 Err(_) => break,
158 };
159 if t == LuaType::Nil {
160 api::set_top(state, -2).ok();
161 break;
162 }
163 out.push(read_any(state, -1));
164 api::set_top(state, -2).ok();
165 i += 1;
166 }
167 out
168}
169
170pub(crate) fn read_map(
172 state: &mut LuaState,
173 idx: i32,
174) -> HashMap<AnyHashableLuaValue, AnyLuaValue> {
175 let table = api::abs_index(state, idx);
176 let mut map = HashMap::new();
177 api::push_nil(state);
178 while api::next(state, table).unwrap_or(false) {
179 let key = read_hashable(state, -2);
180 let value = read_any(state, -1);
181 map.insert(key, value);
182 api::set_top(state, -2).ok();
183 }
184 map
185}