1use std::{
2 collections::{HashMap, VecDeque},
3 path::{Path, PathBuf},
4};
5
6use rbx_dom_weak::{
7 types::{Ref, Variant},
8 ustr, Instance, InstanceBuilder, Ustr, UstrMap, WeakDom,
9};
10
11use crate::{multimap::MultiMap, RojoRef};
12
13use super::{InstanceMetadata, InstanceSnapshot};
14
15#[derive(Debug)]
20pub struct RojoTree {
21 inner: WeakDom,
23
24 metadata_map: HashMap<Ref, InstanceMetadata>,
27
28 path_to_ids: MultiMap<PathBuf, Ref>,
36
37 specified_id_to_refs: MultiMap<RojoRef, Ref>,
42}
43
44impl RojoTree {
45 pub fn new(snapshot: InstanceSnapshot) -> RojoTree {
46 let root_builder = InstanceBuilder::new(snapshot.class_name)
47 .with_name(snapshot.name)
48 .with_properties(snapshot.properties);
49
50 let mut tree = RojoTree {
51 inner: WeakDom::new(root_builder),
52 metadata_map: HashMap::new(),
53 path_to_ids: MultiMap::new(),
54 specified_id_to_refs: MultiMap::new(),
55 };
56
57 let root_ref = tree.inner.root_ref();
58
59 tree.insert_metadata(root_ref, snapshot.metadata);
60
61 for child in snapshot.children {
62 tree.insert_instance(root_ref, child);
63 }
64
65 tree
66 }
67
68 pub fn inner(&self) -> &WeakDom {
69 &self.inner
70 }
71
72 pub fn get_root_id(&self) -> Ref {
73 self.inner.root_ref()
74 }
75
76 #[inline]
78 pub fn root(&self) -> InstanceWithMeta<'_> {
79 self.get_instance(self.get_root_id())
80 .expect("RojoTrees should have a root")
81 }
82
83 pub fn get_instance(&self, id: Ref) -> Option<InstanceWithMeta<'_>> {
84 if let Some(instance) = self.inner.get_by_ref(id) {
85 let metadata = self.metadata_map.get(&id).unwrap();
86
87 Some(InstanceWithMeta { instance, metadata })
88 } else {
89 None
90 }
91 }
92
93 pub fn get_instance_mut(&mut self, id: Ref) -> Option<InstanceWithMetaMut<'_>> {
94 if let Some(instance) = self.inner.get_by_ref_mut(id) {
95 let metadata = self.metadata_map.get_mut(&id).unwrap();
96
97 Some(InstanceWithMetaMut { instance, metadata })
98 } else {
99 None
100 }
101 }
102
103 pub fn insert_instance(&mut self, parent_ref: Ref, snapshot: InstanceSnapshot) -> Ref {
104 let hack_needs_pivot_migration = match snapshot.class_name.as_ref() {
114 "Model" | "Actor" | "Tool" | "HopperBin" | "Flag" | "WorldModel" | "Workspace"
117 | "Status"
118 if !snapshot
119 .properties
120 .contains_key(&ustr("NeedsPivotMigration")) =>
121 {
122 vec![("NeedsPivotMigration", Variant::Bool(false))]
123 }
124 _ => Vec::new(),
125 };
126
127 let builder = InstanceBuilder::empty()
128 .with_class(snapshot.class_name)
129 .with_name(snapshot.name.into_owned())
130 .with_properties(hack_needs_pivot_migration)
131 .with_properties(snapshot.properties);
132
133 let referent = self.inner.insert(parent_ref, builder);
134 self.insert_metadata(referent, snapshot.metadata);
135
136 for child in snapshot.children {
137 self.insert_instance(referent, child);
138 }
139
140 referent
141 }
142
143 pub fn remove(&mut self, id: Ref) {
144 let mut to_move = VecDeque::new();
145 to_move.push_back(id);
146
147 while let Some(id) = to_move.pop_front() {
148 self.remove_metadata(id);
149
150 if let Some(instance) = self.inner.get_by_ref(id) {
151 to_move.extend(instance.children().iter().copied());
152 }
153 }
154
155 self.inner.destroy(id);
156 }
157
158 pub fn update_metadata(&mut self, id: Ref, metadata: InstanceMetadata) {
160 use std::collections::hash_map::Entry;
161
162 match self.metadata_map.entry(id) {
163 Entry::Occupied(mut entry) => {
164 let existing_metadata = entry.get();
165
166 if existing_metadata.relevant_paths != metadata.relevant_paths {
170 for existing_path in &existing_metadata.relevant_paths {
171 self.path_to_ids.remove(existing_path, id);
172 }
173
174 for new_path in &metadata.relevant_paths {
175 self.path_to_ids.insert(new_path.clone(), id);
176 }
177 }
178 if existing_metadata.specified_id != metadata.specified_id {
179 if let Some(new) = &metadata.specified_id {
182 if !self.specified_id_to_refs.get(new).is_empty() {
183 log::error!("Duplicate user-specified referent '{new}'");
184 }
185
186 self.specified_id_to_refs.insert(new.clone(), id);
187 }
188 if let Some(old) = &existing_metadata.specified_id {
189 self.specified_id_to_refs.remove(old, id);
190 }
191 }
192
193 entry.insert(metadata);
194 }
195 Entry::Vacant(entry) => {
196 entry.insert(metadata);
197 }
198 }
199 }
200
201 pub fn descendants(&self, id: Ref) -> RojoDescendants<'_> {
202 let mut queue = VecDeque::new();
203 queue.push_back(id);
204
205 RojoDescendants { queue, tree: self }
206 }
207
208 pub fn get_ids_at_path(&self, path: &Path) -> &[Ref] {
209 self.path_to_ids.get(path)
210 }
211
212 pub fn get_metadata(&self, id: Ref) -> Option<&InstanceMetadata> {
213 self.metadata_map.get(&id)
214 }
215
216 pub fn get_specified_id(&self, specified: &RojoRef) -> Option<Ref> {
219 match self.specified_id_to_refs.get(specified)[..] {
220 [referent] => Some(referent),
221 _ => None,
222 }
223 }
224
225 pub fn set_specified_id(&mut self, id: Ref, specified: RojoRef) {
226 if let Some(metadata) = self.metadata_map.get_mut(&id) {
227 if let Some(old) = metadata.specified_id.replace(specified.clone()) {
228 self.specified_id_to_refs.remove(&old, id);
229 }
230 }
231 self.specified_id_to_refs.insert(specified, id);
232 }
233
234 fn insert_metadata(&mut self, id: Ref, metadata: InstanceMetadata) {
235 for path in &metadata.relevant_paths {
236 self.path_to_ids.insert(path.clone(), id);
237 }
238
239 if let Some(specified_id) = &metadata.specified_id {
240 if !self.specified_id_to_refs.get(specified_id).is_empty() {
241 log::error!("Duplicate user-specified referent '{specified_id}'");
242 }
243
244 self.set_specified_id(id, specified_id.clone());
245 }
246
247 self.metadata_map.insert(id, metadata);
248 }
249
250 fn remove_metadata(&mut self, id: Ref) {
253 let metadata = self.metadata_map.remove(&id).unwrap();
254
255 if let Some(specified) = metadata.specified_id {
256 self.specified_id_to_refs.remove(&specified, id);
257 }
258
259 for path in &metadata.relevant_paths {
260 self.path_to_ids.remove(path, id);
261 }
262 }
263}
264
265pub struct RojoDescendants<'a> {
266 queue: VecDeque<Ref>,
267 tree: &'a RojoTree,
268}
269
270impl<'a> Iterator for RojoDescendants<'a> {
271 type Item = InstanceWithMeta<'a>;
272
273 fn next(&mut self) -> Option<Self::Item> {
274 let id = self.queue.pop_front()?;
275
276 let instance = self
277 .tree
278 .inner
279 .get_by_ref(id)
280 .expect("Instance did not exist");
281
282 let metadata = self
283 .tree
284 .get_metadata(instance.referent())
285 .expect("Metadata did not exist for instance");
286
287 self.queue.extend(instance.children().iter().copied());
288
289 Some(InstanceWithMeta { instance, metadata })
290 }
291}
292
293#[derive(Debug, Clone, Copy)]
299pub struct InstanceWithMeta<'a> {
300 instance: &'a Instance,
301 metadata: &'a InstanceMetadata,
302}
303
304impl<'a> InstanceWithMeta<'a> {
305 pub fn id(&self) -> Ref {
306 self.instance.referent()
307 }
308
309 pub fn parent(&self) -> Ref {
310 self.instance.parent()
311 }
312
313 pub fn name(&self) -> &'a str {
314 &self.instance.name
315 }
316
317 pub fn class_name(&self) -> Ustr {
318 self.instance.class
319 }
320
321 pub fn properties(&self) -> &'a UstrMap<Variant> {
322 &self.instance.properties
323 }
324
325 pub fn children(&self) -> &'a [Ref] {
326 self.instance.children()
327 }
328
329 pub fn metadata(&self) -> &'a InstanceMetadata {
330 self.metadata
331 }
332
333 pub fn inner(&self) -> &Instance {
334 self.instance
335 }
336}
337
338#[derive(Debug)]
344pub struct InstanceWithMetaMut<'a> {
345 instance: &'a mut Instance,
346 metadata: &'a mut InstanceMetadata,
347}
348
349impl InstanceWithMetaMut<'_> {
350 pub fn id(&self) -> Ref {
351 self.instance.referent()
352 }
353
354 pub fn name(&self) -> &str {
355 &self.instance.name
356 }
357
358 pub fn name_mut(&mut self) -> &mut String {
359 &mut self.instance.name
360 }
361
362 pub fn class_name(&self) -> &str {
363 &self.instance.class
364 }
365
366 pub fn set_class_name<'a, S: Into<&'a str>>(&mut self, new_class: S) {
367 self.instance.class = ustr(new_class.into());
368 }
369
370 pub fn properties(&self) -> &UstrMap<Variant> {
371 &self.instance.properties
372 }
373
374 pub fn properties_mut(&mut self) -> &mut UstrMap<Variant> {
375 &mut self.instance.properties
376 }
377
378 pub fn children(&self) -> &[Ref] {
379 self.instance.children()
380 }
381
382 pub fn metadata(&self) -> &InstanceMetadata {
383 self.metadata
384 }
385
386 pub fn inner(&self) -> &Instance {
387 self.instance
388 }
389
390 pub fn inner_mut(&mut self) -> &mut Instance {
391 self.instance
392 }
393}
394
395#[cfg(test)]
396mod test {
397 use crate::{
398 snapshot::{InstanceMetadata, InstanceSnapshot},
399 RojoRef,
400 };
401
402 use super::RojoTree;
403
404 #[test]
405 fn swap_duped_specified_ids() {
406 let custom_ref = RojoRef::new("MyCoolRef".into());
407 let snapshot = InstanceSnapshot::new()
408 .metadata(InstanceMetadata::new().specified_id(Some(custom_ref.clone())));
409 let mut tree = RojoTree::new(InstanceSnapshot::new());
410
411 let original = tree.insert_instance(tree.get_root_id(), snapshot.clone());
412 assert_eq!(tree.get_specified_id(&custom_ref.clone()), Some(original));
413
414 let duped = tree.insert_instance(tree.get_root_id(), snapshot.clone());
415 assert_eq!(tree.get_specified_id(&custom_ref.clone()), None);
416
417 tree.remove(original);
418 assert_eq!(tree.get_specified_id(&custom_ref.clone()), Some(duped));
419 }
420}