Skip to main content

librojo/snapshot/
instance_snapshot.rs

1//! Defines the structure of an instance snapshot.
2
3use std::borrow::Cow;
4
5use rbx_dom_weak::{
6    types::{Ref, Variant},
7    ustr, AHashMap, HashMapExt as _, Instance, Ustr, UstrMap, WeakDom,
8};
9use serde::{Deserialize, Serialize};
10
11use super::InstanceMetadata;
12
13/// A lightweight description of what an instance should look like.
14///
15// Possible future improvements:
16// - Use refcounted/interned strings
17// - Replace use of Variant with a sum of Variant + borrowed value
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct InstanceSnapshot {
20    /// A temporary ID applied to the snapshot that's used for Ref properties.
21    pub snapshot_id: Ref,
22
23    /// Rojo-specific metadata associated with the instance.
24    pub metadata: InstanceMetadata,
25
26    /// Correpsonds to the Name property of the instance.
27    pub name: Cow<'static, str>,
28
29    /// Corresponds to the ClassName property of the instance.
30    pub class_name: Ustr,
31
32    /// All other properties of the instance, weakly-typed.
33    pub properties: UstrMap<Variant>,
34
35    /// The children of the instance represented as more snapshots.
36    ///
37    /// Order is relevant for Roblox instances!
38    pub children: Vec<InstanceSnapshot>,
39}
40
41impl InstanceSnapshot {
42    pub fn new() -> Self {
43        Self {
44            snapshot_id: Ref::none(),
45            metadata: InstanceMetadata::default(),
46            name: Cow::Borrowed("DEFAULT"),
47            class_name: ustr("DEFAULT"),
48            properties: UstrMap::new(),
49            children: Vec::new(),
50        }
51    }
52
53    pub fn name(self, name: impl Into<String>) -> Self {
54        Self {
55            name: Cow::Owned(name.into()),
56            ..self
57        }
58    }
59
60    pub fn class_name<S: Into<Ustr>>(self, class_name: S) -> Self {
61        Self {
62            class_name: class_name.into(),
63            ..self
64        }
65    }
66
67    pub fn property<K, V>(mut self, key: K, value: V) -> Self
68    where
69        K: Into<Ustr>,
70        V: Into<Variant>,
71    {
72        self.properties.insert(key.into(), value.into());
73        self
74    }
75
76    pub fn properties(self, properties: impl Into<UstrMap<Variant>>) -> Self {
77        Self {
78            properties: properties.into(),
79            ..self
80        }
81    }
82
83    pub fn children(self, children: impl Into<Vec<Self>>) -> Self {
84        Self {
85            children: children.into(),
86            ..self
87        }
88    }
89
90    pub fn snapshot_id(self, snapshot_id: Ref) -> Self {
91        Self {
92            snapshot_id,
93            ..self
94        }
95    }
96
97    pub fn metadata(self, metadata: impl Into<InstanceMetadata>) -> Self {
98        Self {
99            metadata: metadata.into(),
100            ..self
101        }
102    }
103
104    #[profiling::function]
105    pub fn from_tree(tree: WeakDom, id: Ref) -> Self {
106        let (_, mut raw_tree) = tree.into_raw();
107        Self::from_raw_tree(&mut raw_tree, id)
108    }
109
110    fn from_raw_tree(raw_tree: &mut AHashMap<Ref, Instance>, id: Ref) -> Self {
111        let instance = raw_tree
112            .remove(&id)
113            .expect("instance did not exist in tree");
114
115        let children = instance
116            .children()
117            .iter()
118            .map(|&id| Self::from_raw_tree(raw_tree, id))
119            .collect();
120
121        Self {
122            snapshot_id: id,
123            metadata: InstanceMetadata::default(),
124            name: Cow::Owned(instance.name),
125            class_name: instance.class,
126            properties: instance.properties,
127            children,
128        }
129    }
130}
131
132impl Default for InstanceSnapshot {
133    fn default() -> Self {
134        Self::new()
135    }
136}