1use core::cell::Cell;
2use core::fmt;
3
4use crate as rune;
5use crate::alloc::prelude::*;
6use crate::alloc::try_vec;
7use crate::alloc::{self, HashMap, Vec};
8#[cfg(feature = "emit")]
9use crate::compile::Location;
10use crate::compile::{self, Visibility};
11use crate::parse::{Parse, Parser};
12use crate::{Hash, Item, ItemBuf};
13
14#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[try_clone(copy)]
17#[repr(transparent)]
18pub(crate) struct ModId(u32);
19
20impl fmt::Display for ModId {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 self.0.fmt(f)
23 }
24}
25
26impl fmt::Debug for ModId {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 self.0.fmt(f)
29 }
30}
31
32#[derive(Default, TryClone, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34#[try_clone(copy)]
35#[repr(transparent)]
36pub struct ItemId(u32);
37
38impl ItemId {
39 pub(crate) const ROOT: ItemId = ItemId(0);
41}
42
43impl Parse for ItemId {
44 #[inline]
45 fn parse(_: &mut Parser<'_>) -> compile::Result<Self> {
46 Ok(ItemId::ROOT)
47 }
48}
49
50impl fmt::Debug for ItemId {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 self.0.fmt(f)
53 }
54}
55
56impl fmt::Display for ItemId {
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 self.0.fmt(f)
59 }
60}
61
62#[derive(Debug)]
64#[non_exhaustive]
65pub(crate) struct ModMeta {
66 #[cfg(feature = "emit")]
68 pub(crate) location: Location,
69 pub(crate) item: ItemId,
71 pub(crate) visibility: Visibility,
73 pub(crate) parent: Option<ModId>,
75}
76
77impl ModMeta {
78 pub(crate) fn is_public(&self, pool: &Pool) -> bool {
80 let mut current = Some(self);
81
82 while let Some(m) = current.take() {
83 if !m.visibility.is_public() {
84 return false;
85 }
86
87 current = m.parent.map(|id| pool.module(id));
88 }
89
90 true
91 }
92}
93
94macro_rules! alloc_item {
95 ($self:expr, $item:expr) => {{
96 let item = $item;
97 let hash = Hash::type_hash(item);
98
99 match $self.hash_to_item.get(&hash) {
100 Some(id) => *id,
101 None => {
102 let id = ItemId(u32::try_from($self.items.len()).expect("ran out of item ids"));
103 let item = $item.try_to_owned()?;
104 $self.items.try_push(ItemStorage {
105 hash,
106 item,
107 ids: Cell::new(0),
108 })?;
109 $self.hash_to_item.try_insert(hash, id)?;
110 id
111 }
112 }
113 }};
114}
115
116struct ItemStorage {
117 hash: Hash,
118 item: ItemBuf,
119 ids: Cell<usize>,
121}
122
123pub(crate) struct Pool {
125 modules: Vec<ModMeta>,
126 items: Vec<ItemStorage>,
127 item_to_mod: HashMap<ItemId, ModId>,
128 hash_to_item: HashMap<Hash, ItemId>,
129}
130
131impl Pool {
132 pub fn new() -> alloc::Result<Self> {
133 let root_hash: Hash = Hash::type_hash(Item::new());
134
135 Ok(Self {
136 modules: Vec::new(),
137 items: try_vec![ItemStorage {
138 hash: root_hash,
139 item: ItemBuf::new(),
140 ids: Cell::new(0),
141 }],
142 item_to_mod: HashMap::new(),
143 hash_to_item: HashMap::try_from_iter([(root_hash, ItemId(0))])?,
144 })
145 }
146
147 pub(crate) fn next_id(&self, id: ItemId) -> usize {
149 let storage = self.item_storage(id);
150 let id = storage.ids.get();
151 storage.ids.set(id + 1);
152 id
153 }
154
155 pub(crate) fn item(&self, id: ItemId) -> &Item {
157 &self.item_storage(id).item
158 }
159
160 pub(crate) fn item_type_hash(&self, id: ItemId) -> Hash {
162 self.item_storage(id).hash
163 }
164
165 pub(crate) fn item_for_hash(&self, hash: Hash) -> Option<&Item> {
167 Some(self.item(*self.hash_to_item.get(&hash)?))
168 }
169
170 pub(crate) fn module(&self, ModId(id): ModId) -> &ModMeta {
172 let id = usize::try_from(id).expect("module id overflow");
173
174 match self.modules.get(id) {
175 Some(item) => item,
176 None => panic!("missing module by id {id}"),
177 }
178 }
179
180 pub(crate) fn module_item(&self, id: ModId) -> &Item {
182 let id = self.module(id).item;
183 self.item(id)
184 }
185
186 pub(crate) fn module_item_hash(&self, id: ModId) -> Hash {
188 let id = self.module(id).item;
189 self.item_type_hash(id)
190 }
191
192 pub(crate) fn module_by_item(&self, id: ItemId) -> Option<&ModMeta> {
194 Some(self.module(*self.item_to_mod.get(&id)?))
195 }
196
197 pub(crate) fn alloc_module(&mut self, item: ModMeta) -> alloc::Result<ModId> {
199 if let Some(id) = self.item_to_mod.get(&item.item) {
200 return Ok(*id);
201 }
202
203 let id = ModId(u32::try_from(self.modules.len()).expect("ran out of item ids"));
204 self.item_to_mod.try_insert(item.item, id)?;
205 self.modules.try_push(item)?;
206 Ok(id)
207 }
208
209 pub(crate) fn alloc_item<T>(&mut self, item: T) -> alloc::Result<ItemId>
211 where
212 T: AsRef<Item>,
213 {
214 Ok(alloc_item!(self, item.as_ref()))
215 }
216
217 pub(crate) fn try_map_alloc<M>(&mut self, id: ItemId, m: M) -> alloc::Result<Option<ItemId>>
219 where
220 M: FnOnce(&Item) -> Option<&Item>,
221 {
222 let Some(item) = m(self.item(id)) else {
223 return Ok(None);
224 };
225
226 Ok(Some(alloc_item!(self, item)))
227 }
228
229 fn item_storage(&self, ItemId(id): ItemId) -> &ItemStorage {
230 let id = usize::try_from(id).expect("item id overflow");
231
232 match self.items.get(id) {
233 Some(item) => item,
234 None => panic!("missing item by id {id}"),
235 }
236 }
237}