lucia_lang/objects/
table.rs1use std::{
2 hash::{Hash, Hasher},
3 mem,
4};
5
6use gc_arena::{lock::RefLock, Collect, Gc, Mutation};
7use indexmap::IndexMap;
8
9use crate::{
10 objects::{IntoValue, Value},
11 Context,
12};
13
14#[derive(Debug, Copy, Clone, Collect)]
15#[collect(no_drop)]
16pub struct Table<'gc>(pub Gc<'gc, RefLock<TableState<'gc>>>);
17
18impl<'gc> PartialEq for Table<'gc> {
19 fn eq(&self, other: &Table<'gc>) -> bool {
20 Gc::ptr_eq(self.0, other.0)
21 }
22}
23
24impl<'gc> Eq for Table<'gc> {}
25
26impl<'gc> Hash for Table<'gc> {
27 fn hash<H: Hasher>(&self, state: &mut H) {
28 self.0.as_ptr().hash(state);
29 }
30}
31
32impl<'gc> Table<'gc> {
33 pub fn new(mc: &Mutation<'gc>) -> Self {
34 Table::from(mc, TableState::default())
35 }
36
37 pub fn from(mc: &Mutation<'gc>, table_state: TableState<'gc>) -> Self {
38 Table(Gc::new(mc, RefLock::new(table_state)))
39 }
40
41 pub fn get<K: IntoValue<'gc>>(&self, ctx: Context<'gc>, key: K) -> Value<'gc> {
42 let key = key.into_value(ctx);
43 let entries = &self.0.borrow().entries;
44 if let Value::Int(key) = key {
45 if let Ok(key) = usize::try_from(key) {
46 return *entries.array.get(key).unwrap_or(&Value::Null);
47 }
48 }
49 *entries.map.get(&key).unwrap_or(&Value::Null)
50 }
51
52 pub fn get_index(&self, index: usize) -> Option<(Value<'gc>, Value<'gc>)> {
53 let entries = &self.0.borrow().entries;
54 if index < entries.array.len() {
55 entries
56 .array
57 .get(index)
58 .map(|v| (Value::Int(index.try_into().unwrap()), *v))
59 } else {
60 entries
61 .map
62 .get_index(index - entries.array.len())
63 .map(|(k, v)| (*k, *v))
64 }
65 }
66
67 pub fn set<K: IntoValue<'gc>, V: IntoValue<'gc>>(&self, ctx: Context<'gc>, key: K, value: V) {
68 let key = key.into_value(ctx);
69 let value = value.into_value(ctx);
70 let entries = &mut self.0.borrow_mut(&ctx).entries;
71 if let Value::Int(k) = key {
72 if let Ok(k) = usize::try_from(k) {
73 match k.cmp(&entries.array.len()) {
74 std::cmp::Ordering::Less => return entries.array[k] = value,
75 std::cmp::Ordering::Equal => return entries.array.push(value),
76 std::cmp::Ordering::Greater => (),
77 }
78 }
79 }
80 if value.is_null() {
81 entries.map.remove(&key);
82 } else {
83 entries.map.insert(key, value);
84 }
85 }
86
87 pub fn len(&self) -> usize {
88 let entries = &self.0.borrow().entries;
89 entries.array.len() + entries.map.len()
90 }
91
92 pub fn is_empty(&self) -> bool {
93 self.len() == 0
94 }
95
96 pub fn metatable(&self) -> Option<Table<'gc>> {
97 self.0.borrow().metatable
98 }
99
100 pub fn set_metatable(
101 &self,
102 mc: &Mutation<'gc>,
103 metatable: Option<Table<'gc>>,
104 ) -> Option<Table<'gc>> {
105 mem::replace(&mut self.0.borrow_mut(mc).metatable, metatable)
106 }
107
108 pub(crate) fn repr_table(&self, t: &Value<'gc>) -> String {
110 let mut temp = Vec::new();
111 for i in 0..self.len() {
112 if let Some((k, v)) = self.get_index(i) {
113 temp.push(format!(
114 "{}: {}",
115 if k.is(t) {
116 "<table>".to_string()
117 } else if let Value::Table(k_t) = k {
118 k_t.repr_table(t)
119 } else {
120 k.repr()
121 },
122 if v.is(t) {
123 "<table>".to_string()
124 } else if let Value::Table(v_t) = v {
125 v_t.repr_table(t)
126 } else {
127 v.repr()
128 },
129 ))
130 }
131 }
132 format!("{{{}}}", temp.join(", "))
133 }
134}
135
136#[derive(Debug, Default, Collect)]
137#[collect(no_drop)]
138pub struct TableState<'gc> {
139 pub entries: TableEntries<'gc>,
140 pub metatable: Option<Table<'gc>>,
141}
142
143#[derive(Debug, Default)]
144pub struct TableEntries<'gc> {
145 array: Vec<Value<'gc>>,
146 map: IndexMap<Value<'gc>, Value<'gc>>,
147}
148
149unsafe impl<'gc> Collect for TableEntries<'gc> {
150 fn trace(&self, cc: &gc_arena::Collection) {
151 self.array.trace(cc);
152 for (key, value) in self.map.iter() {
153 key.trace(cc);
154 value.trace(cc);
155 }
156 }
157}
158
159impl<'gc> FromIterator<Value<'gc>> for TableEntries<'gc> {
160 fn from_iter<T: IntoIterator<Item = Value<'gc>>>(iter: T) -> Self {
161 let array = Vec::from_iter(iter);
162 TableEntries {
163 array,
164 map: IndexMap::new(),
165 }
166 }
167}
168
169impl<'gc> FromIterator<(Value<'gc>, Value<'gc>)> for TableEntries<'gc> {
170 fn from_iter<T: IntoIterator<Item = (Value<'gc>, Value<'gc>)>>(iter: T) -> Self {
171 let map = IndexMap::from_iter(iter);
172 TableEntries {
173 array: Vec::new(),
174 map,
175 }
176 }
177}