1use std::panic::{catch_unwind, AssertUnwindSafe};
2
3use crate::{api::AccessApi, state::LuaState, value::value::LuaValue};
4
5use super::CallApi;
6
7pub trait MiscApi {
8 fn len(&mut self, idx: isize);
9 fn concat(&mut self, n: usize);
10 fn next(&mut self, idx: isize) -> bool;
11 fn error(&mut self) -> isize;
12 fn pcall(&mut self, n_args: isize, n_results: isize, msg: isize) -> isize;
13}
14
15impl MiscApi for LuaState {
16 fn len(&mut self, idx: isize) {
17 let val = self.stack_get(idx);
18 match val {
19 LuaValue::Str(s) => self.stack_push(LuaValue::Integer(s.len() as i64)),
20 LuaValue::Table(tb) => self.stack_push(LuaValue::Integer(tb.len() as i64)),
21 _ => {
22 let result = self.call_meta_method(val.clone(), val, "__len");
23 if let Some(LuaValue::Integer(i)) = result {
24 self.stack_push(LuaValue::Integer(i))
25 } else {
26 panic!("length error!")
27 }
28 }
29 }
30 }
31
32 fn concat(&mut self, n: usize) {
33 if n == 0 {
34 self.stack_push(LuaValue::Str("".into()))
35 } else if n >= 2 {
36 for _ in 1..n {
37 if self.is_string(-1) && self.is_string(-2) {
38 let s2 = self.to_string(-1);
39 let s1 = self.to_string(-2);
40 self.stack_pop();
41 self.stack_pop();
42 self.stack_push(LuaValue::Str(format!("{}{}", s1, s2)));
43 continue;
44 }
45 let b = self.stack_pop();
46 let a = self.stack_pop();
47 let result = self.call_meta_method(a, b, "__concat");
48 if let Some(r) = result {
49 self.stack_push(r);
50 continue;
51 }
52 panic!("concatenation error!")
53 }
54 }
55 }
56
57 fn next(&mut self, idx: isize) -> bool {
58 let val = self.stack_get(idx);
59 if let LuaValue::Table(tbl) = val {
60 let key = self.stack_pop();
61 let next_key = tbl.next_key(&key);
62 if !next_key.is_nil() {
63 let next_val = tbl.get(&next_key);
64 self.stack_push(next_key);
65 self.stack_push(next_val);
66 true
67 } else {
68 false
69 }
70 } else {
71 panic!("table expected!")
72 }
73 }
74
75 fn error(&mut self) -> isize {
76 let val = self.stack_pop();
77 panic!("{:?}", val)
78 }
79
80 fn pcall(&mut self, n_args: isize, n_results: isize, msgh: isize) -> isize {
81 let node = self.get_node().clone();
82 let caller = node.get_stack();
83 let mut status = 2;
84
85 let result = catch_unwind(AssertUnwindSafe(|| {
86 self.call(n_args, n_results);
87 status = 0;
88 }));
89 if let Err(err) = result {
90 if msgh != 0 {
91 panic!("{:?}", err);
92 }
93 let stack = node.get_stack();
94 while *stack != *caller {
95 self.pop_lua_stack()
96 }
97 let err_msg = if let Some(err_msg) = err.downcast_ref::<&str>() {
98 err_msg.to_string()
99 } else if let Some(err_msg) = err.downcast_ref::<String>() {
100 err_msg.clone()
101 } else {
102 "Unknown error".to_string()
103 };
104 self.stack_push(LuaValue::Str(err_msg))
105 }
106 status
107 }
108}