lucia_lang/objects/
table.rs

1use 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    /// Return the repr string fo the table.
109    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}