1use fbxscii::ElementAttribute;
8
9use crate::document::{
10 Document, LazyObject, ObjectPropertyConnection, Property, PropertyDetails, Template,
11};
12use std::collections::HashMap;
13
14#[derive(Debug, PartialEq)]
15pub enum ObjectError {
16 MissingTemplate(String),
17}
18
19#[derive(Debug)]
21pub struct Object<'a> {
22 document: &'a Document,
23 template: &'a Template,
24 object: &'a LazyObject,
25 index: u64,
27}
28
29impl<'a> Object<'a> {
30 pub fn name(&self) -> &str {
31 &self.object.name
32 }
33
34 pub fn type_name(&self) -> &str {
35 &self.object.type_name
36 }
37
38 pub fn class_name(&self) -> &str {
39 &self.object.class_name
40 }
41
42 pub fn object_index(&self) -> u64 {
43 self.index
44 }
45
46 pub fn element(&self) -> Option<&'a fbxscii::Element> {
47 self.document
48 .object_element_amphitheatre
49 .get(self.object.element_index)
50 }
51
52 pub fn connected_object_ids(&self) -> &[u64] {
54 self.document
55 .object_connections
56 .get(&self.index)
57 .map(|v| v.as_slice())
58 .unwrap_or(&[])
59 }
60
61 pub fn object_property_targets(&self) -> &[ObjectPropertyConnection] {
64 self.document
65 .object_property_connections
66 .get(&self.index)
67 .map(|v| v.as_slice())
68 .unwrap_or(&[])
69 }
70
71 pub fn pp_source_property_names(&self) -> &[String] {
73 self.document
74 .object_to_source_properties
75 .get(&self.index)
76 .map(|v| v.as_slice())
77 .unwrap_or(&[])
78 }
79
80 pub fn pp_targets(&self, key: (u64, &str)) -> Option<&[ObjectPropertyConnection]> {
86 let (source_object, source_property) = key;
87 self.document
88 .property_connections
89 .get(&ObjectPropertyConnection {
90 dest: source_object,
91 property: source_property.to_string(),
92 })
93 .map(|v| v.as_slice())
94 }
95}
96
97impl<'a> Object<'a> {
98 pub fn new(
99 document: &'a Document,
100 template: &'a Template,
101 object: &'a LazyObject,
102 index: u64,
103 ) -> Self {
104 Self {
105 document,
106 template,
107 object,
108 index,
109 }
110 }
111
112 pub fn template(&self) -> &'a Template {
113 self.template
114 }
115
116 pub fn properties(&self) -> HashMap<String, Property> {
117 let object_index = self.object.element_index;
118 let object_handle = self
119 .document
120 .object_element_amphitheatre
121 .get_handle(object_index);
122 if object_handle.is_none() {
123 return HashMap::new();
124 }
125 let object_handle = object_handle.unwrap();
126 let property_table_handle_opt = object_handle.first_child_by_key("Properties70");
127 let mut properties = HashMap::new();
128 if let Some(property_table_handle) = property_table_handle_opt {
129 for property_detail in property_table_handle.children() {
130 let r: Result<PropertyDetails, _> = property_detail.try_into();
131 if let Ok(property_details) = r {
132 properties.insert(property_details.name, property_details.property);
133 }
134 }
135 }
136 properties
137 }
138
139 pub fn attributes(&self) -> HashMap<String, ElementAttribute> {
140 let object_index = self.object.element_index;
141 let object_handle = self
142 .document
143 .object_element_amphitheatre
144 .get_handle(object_index);
145 if object_handle.is_none() {
146 return HashMap::new();
147 }
148 let object_handle = object_handle.unwrap();
149 let mut attributes = HashMap::new();
150 for attribute in object_handle.children() {
151 if attribute.key() == "Properties70" {
152 continue;
153 }
154 let subtree = self
155 .document
156 .object_element_amphitheatre
157 .extract_subtree(attribute.index());
158 if subtree.is_none() {
159 continue;
160 }
161 let subtree = subtree.unwrap();
162 attributes.insert(attribute.key().to_string(), subtree);
163 }
164 attributes
165 }
166}
167
168#[derive(Debug, PartialEq)]
173pub struct OwnedObject {
174 pub object_index: u64,
176 pub name: String,
177 pub type_name: String,
178 pub class_name: String,
179 pub properties: HashMap<String, Property>,
180 pub attributes: HashMap<String, ElementAttribute>,
181 pub connected_object_ids: Vec<u64>,
183 pub object_property_targets: Vec<ObjectPropertyConnection>,
185 pub pp_property_targets: HashMap<String, ObjectPropertyConnection>,
188}
189
190impl<'a> From<Object<'a>> for OwnedObject {
191 fn from(object: Object<'a>) -> Self {
192 let idx = object.object_index();
193 let mut pp_property_targets = HashMap::new();
194 for source_property in object.pp_source_property_names() {
195 let Some(targets) = object.pp_targets((idx, source_property.as_str())) else {
196 continue;
197 };
198 if let Some(first) = targets.first() {
199 pp_property_targets.insert(source_property.clone(), first.clone());
200 }
201 }
202
203 Self {
204 object_index: idx,
205 name: object.name().to_string(),
206 type_name: object.type_name().to_string(),
207 class_name: object.class_name().to_string(),
208 properties: object.properties(),
209 attributes: object.attributes(),
210 connected_object_ids: object.connected_object_ids().to_vec(),
211 object_property_targets: object.object_property_targets().to_vec(),
212 pp_property_targets,
213 }
214 }
215}
216
217#[derive(Clone, Debug)]
218pub struct Objects<'a> {
219 pub(crate) iter: std::collections::hash_map::Iter<'a, u64, LazyObject>,
221
222 pub(crate) document: &'a Document,
224}
225
226impl ExactSizeIterator for Objects<'_> {}
227impl<'a> Iterator for Objects<'a> {
228 type Item = Result<Object<'a>, ObjectError>;
229 fn next(&mut self) -> Option<Self::Item> {
230 self.iter.next().map(|(index, object)| {
231 self.document
232 .template_for_object(object)
233 .map(|template| Object::new(self.document, template, object, *index))
234 .ok_or_else(|| ObjectError::MissingTemplate(object.type_name.clone()))
235 })
236 }
237 fn size_hint(&self) -> (usize, Option<usize>) {
238 self.iter.size_hint()
239 }
240 fn count(self) -> usize {
241 self.iter.count()
242 }
243 fn last(self) -> Option<Self::Item> {
244 let document = self.document;
245 self.iter.last().map(|(index, object)| {
246 document
247 .template_for_object(object)
248 .map(|template| Object::new(document, template, object, *index))
249 .ok_or_else(|| ObjectError::MissingTemplate(object.type_name.clone()))
250 })
251 }
252 fn nth(&mut self, n: usize) -> Option<Self::Item> {
253 self.iter.nth(n).map(|(index, object)| {
254 self.document
255 .template_for_object(object)
256 .map(|template| Object::new(self.document, template, object, *index))
257 .ok_or_else(|| ObjectError::MissingTemplate(object.type_name.clone()))
258 })
259 }
260}