1mod array;
22mod async_;
23mod bindings;
24
25use crate::errors::Result;
26pub use crate::ffi::enums::StorageType;
27pub use crate::ffi::AttributeInfo;
28use crate::node::HoudiniNode;
29use crate::stringhandle::{StringArray, StringHandle};
30pub use array::*;
31use async_::AsyncAttribResult;
32use std::any::Any;
33use std::borrow::Cow;
34use std::ffi::{CStr, CString};
35use std::marker::PhantomData;
36
37pub type JobId = i32;
38
39mod private {
40 pub trait Sealed {}
41}
42pub trait AttribAccess: private::Sealed + Clone + Default + Send + Sized + 'static {
43 fn storage() -> StorageType;
44 fn storage_array() -> StorageType;
45 fn get(
46 name: &CStr,
47 node: &HoudiniNode,
48 info: &AttributeInfo,
49 part_id: i32,
50 buffer: &mut Vec<Self>,
51 ) -> Result<()>;
52 fn get_async(
53 name: &CStr,
54 node: &HoudiniNode,
55 info: &AttributeInfo,
56 part_id: i32,
57 buffer: &mut Vec<Self>,
58 ) -> Result<JobId>;
59 fn set(
60 name: &CStr,
61 node: &HoudiniNode,
62 info: &AttributeInfo,
63 part_id: i32,
64 data: &[Self],
65 start: i32,
66 len: i32,
67 ) -> Result<()>;
68
69 fn set_async(
70 name: &CStr,
71 node: &HoudiniNode,
72 info: &AttributeInfo,
73 part: i32,
74 data: &[Self],
75 start: i32,
76 len: i32,
77 ) -> Result<JobId>;
78
79 fn set_unique(
80 name: &CStr,
81 node: &HoudiniNode,
82 info: &AttributeInfo,
83 part_id: i32,
84 data: &[Self],
85 start: i32,
86 ) -> Result<()>;
87
88 fn set_unique_async(
89 name: &CStr,
90 node: &HoudiniNode,
91 info: &AttributeInfo,
92 part_id: i32,
93 data: &[Self],
94 start: i32,
95 ) -> Result<JobId>;
96
97 fn get_array(
98 name: &CStr,
99 node: &HoudiniNode,
100 info: &AttributeInfo,
101 part: i32,
102 ) -> Result<DataArray<'static, Self>>
103 where
104 [Self]: ToOwned<Owned = Vec<Self>>;
105
106 fn get_array_async(
107 name: &CStr,
108 node: &HoudiniNode,
109 info: &AttributeInfo,
110 data: &mut [Self],
111 sizes: &mut [i32],
112 part: i32,
113 ) -> Result<JobId>;
114 fn set_array(
115 name: &CStr,
116 node: &HoudiniNode,
117 info: &AttributeInfo,
118 part: i32,
119 data: &[Self],
120 sizes: &[i32],
121 ) -> Result<()>
122 where
123 [Self]: ToOwned<Owned = Vec<Self>>;
124
125 fn set_array_async(
126 name: &CStr,
127 node: &HoudiniNode,
128 info: &AttributeInfo,
129 part: i32,
130 data: &[Self],
131 sizes: &[i32],
132 ) -> Result<JobId>;
133}
134
135macro_rules! impl_sealed {
136 ($($x:ident),+ $(,)?) => {
137 $(impl private::Sealed for $x {})+
138 }
139}
140
141impl_sealed!(u8, i8, i16, i32, i64, f32, f64);
142
143impl StorageType {
144 pub(crate) fn type_matches(&self, other: StorageType) -> bool {
148 use StorageType::*;
149 match other {
150 IntArray | Uint8Array | Int8Array | Int16Array | Int64Array => {
151 matches!(*self, Int | Uint8 | Int16 | Int64)
152 }
153 FloatArray | Float64Array => matches!(*self, Float | Float64),
154 StringArray => matches!(*self, StringArray),
155 _st => matches!(*self, _st),
156 }
157 }
158}
159
160pub(crate) struct AttributeBundle {
161 pub(crate) info: AttributeInfo,
162 pub(crate) name: CString,
163 pub(crate) node: HoudiniNode,
164}
165
166pub struct NumericAttr<T: AttribAccess>(pub(crate) AttributeBundle, PhantomData<T>);
167
168pub struct NumericArrayAttr<T: AttribAccess>(pub(crate) AttributeBundle, PhantomData<T>);
169
170pub struct StringAttr(pub(crate) AttributeBundle);
171
172pub struct StringArrayAttr(pub(crate) AttributeBundle);
173
174pub struct DictionaryAttr(pub(crate) AttributeBundle);
175
176pub struct DictionaryArrayAttr(pub(crate) AttributeBundle);
177
178impl<T: AttribAccess> NumericArrayAttr<T>
179where
180 [T]: ToOwned<Owned = Vec<T>>,
181{
182 pub(crate) fn new(
183 name: CString,
184 info: AttributeInfo,
185 node: HoudiniNode,
186 ) -> NumericArrayAttr<T> {
187 NumericArrayAttr(AttributeBundle { info, name, node }, PhantomData)
188 }
189 pub fn get(&self, part_id: i32) -> Result<DataArray<T>> {
190 debug_assert!(self.0.info.storage().type_matches(T::storage()));
191 T::get_array(&self.0.name, &self.0.node, &self.0.info, part_id)
192 }
193
194 pub fn get_async(&self, part_id: i32) -> Result<(JobId, DataArray<T>)> {
195 let info = &self.0.info;
196 debug_assert!(info.storage().type_matches(T::storage()));
197 let mut data = vec![T::default(); info.total_array_elements() as usize];
198 let mut sizes = vec![0i32; info.count() as usize];
199 let job_id = T::get_array_async(
200 &self.0.name,
201 &self.0.node,
202 info,
203 &mut data,
204 &mut sizes,
205 part_id,
206 )?;
207 Ok((job_id, DataArray::new_owned(data, sizes)))
208 }
209
210 pub fn set(&self, part_id: i32, values: &DataArray<T>) -> Result<()> {
211 debug_assert!(self.0.info.storage().type_matches(T::storage()));
212 debug_assert_eq!(
213 self.0.info.count(),
214 values.sizes().len() as i32,
215 "sizes array must be the same as AttributeInfo::count"
216 );
217 debug_assert_eq!(
218 self.0.info.total_array_elements(),
219 values.data().len() as i64,
220 "data array must be the same as AttributeInfo::total_array_elements"
221 );
222 T::set_array(
223 &self.0.name,
224 &self.0.node,
225 &self.0.info,
226 part_id,
227 values.data(),
228 values.sizes(),
229 )
230 }
231}
232
233impl<T: AttribAccess> NumericAttr<T> {
234 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> NumericAttr<T> {
235 NumericAttr(AttributeBundle { info, name, node }, PhantomData)
236 }
237 pub fn get(&self, part_id: i32) -> Result<Vec<T>> {
239 debug_assert_eq!(self.0.info.storage(), T::storage());
240 let mut buffer = vec![];
241 T::get(
242 &self.0.name,
243 &self.0.node,
244 &self.0.info,
245 part_id,
246 &mut buffer,
247 )?;
248 Ok(buffer)
249 }
250 pub fn read_async_into(&self, part_id: i32, buffer: &mut Vec<T>) -> Result<i32> {
253 let info = &self.0.info;
257 buffer.resize((info.count() * info.tuple_size()) as usize, T::default());
258 T::get_async(&self.0.name, &self.0.node, &self.0.info, part_id, buffer)
259 }
260
261 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<T>> {
262 let info = &self.0.info;
263 let size = (info.count() * info.tuple_size()) as usize;
264 let mut data = Vec::<T>::with_capacity(size);
265 let job_id = T::get_async(&self.0.name, &self.0.node, info, part_id, &mut data)?;
266 Ok(AsyncAttribResult {
267 job_id,
268 data,
269 size,
270 session: self.0.node.session.clone(),
271 })
272 }
273
274 pub fn read_into(&self, part_id: i32, buffer: &mut Vec<T>) -> Result<()> {
277 debug_assert_eq!(self.0.info.storage(), T::storage());
278 let info = AttributeInfo::new(&self.0.node, part_id, self.0.info.owner(), &self.0.name)?;
280 T::get(&self.0.name, &self.0.node, &info, part_id, buffer)
281 }
282 pub fn set(&self, part_id: i32, values: &[T]) -> Result<()> {
283 debug_assert_eq!(self.0.info.storage(), T::storage());
284 T::set(
285 &self.0.name,
286 &self.0.node,
287 &self.0.info,
288 part_id,
289 values,
290 0,
291 self.0.info.count().min(values.len() as i32),
292 )
293 }
294
295 pub fn set_unique(&self, part_id: i32, value: &[T]) -> Result<()> {
298 debug_assert_eq!(self.0.info.storage(), T::storage());
299 debug_assert!(value.len() <= self.0.info.tuple_size() as usize);
300 T::set_unique(&self.0.name, &self.0.node, &self.0.info, part_id, value, 0)
301 }
302}
303
304impl StringAttr {
305 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringAttr {
306 StringAttr(AttributeBundle { info, name, node })
307 }
308 pub fn get(&self, part_id: i32) -> Result<StringArray> {
309 debug_assert!(self.0.node.is_valid()?);
310 bindings::get_attribute_string_data(
311 &self.0.node,
312 part_id,
313 self.0.name.as_c_str(),
314 &self.0.info.0,
315 )
316 }
317
318 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
319 bindings::get_attribute_string_data_async(
320 &self.0.node,
321 part_id,
322 self.0.name.as_c_str(),
323 &self.0.info.0,
324 )
325 }
326
327 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
328 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
329 bindings::set_attribute_string_data(
330 &self.0.node,
331 part_id,
332 self.0.name.as_c_str(),
333 &self.0.info.0,
334 ptrs.as_mut(),
335 )
336 }
337
338 pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
339 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
340 bindings::set_attribute_string_data_async(
341 &self.0.node,
342 self.0.name.as_c_str(),
343 part_id,
344 &self.0.info.0,
345 ptrs.as_mut(),
346 )
347 }
348 pub fn set_unique(&self, part: i32, value: &CStr) -> Result<()> {
351 bindings::set_attribute_string_unique_data(
352 &self.0.node,
353 self.0.name.as_c_str(),
354 &self.0.info.0,
355 part,
356 value.as_ptr(),
357 )
358 }
359
360 pub fn set_unique_async(&self, part: i32, value: &CStr) -> Result<JobId> {
362 bindings::set_attribute_string_unique_data_async(
363 &self.0.node,
364 self.0.name.as_c_str(),
365 &self.0.info.0,
366 part,
367 value.as_ptr(),
368 )
369 }
370
371 pub fn set_indexed(
373 &self,
374 part_id: i32,
375 values: &[impl AsRef<CStr>],
376 indices: &[i32],
377 ) -> Result<()> {
378 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
379 bindings::set_attribute_indexed_string_data(
380 &self.0.node,
381 part_id,
382 self.0.name.as_c_str(),
383 &self.0.info.0,
384 ptrs.as_mut(),
385 indices,
386 )
387 }
388
389 pub fn set_indexed_async(
390 &self,
391 part_id: i32,
392 values: &[impl AsRef<CStr>],
393 indices: &[i32],
394 ) -> Result<JobId> {
395 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
396 bindings::set_attribute_indexed_string_data_async(
397 &self.0.node,
398 part_id,
399 self.0.name.as_c_str(),
400 &self.0.info.0,
401 ptrs.as_mut(),
402 indices,
403 )
404 }
405}
406
407impl StringArrayAttr {
408 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringArrayAttr {
409 StringArrayAttr(AttributeBundle { info, name, node })
410 }
411 pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
412 debug_assert!(self.0.node.is_valid()?);
413 bindings::get_attribute_string_array_data(
414 &self.0.node,
415 self.0.name.as_c_str(),
416 part_id,
417 &self.0.info,
418 )
419 }
420
421 pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
422 let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
423 let mut sizes = vec![0; self.0.info.count() as usize];
424 let job_id = bindings::get_attribute_string_array_data_async(
425 &self.0.node,
426 self.0.name.as_c_str(),
427 part_id,
428 &self.0.info.0,
429 &mut handles,
430 &mut sizes,
431 )?;
432 Ok((
433 job_id,
434 StringMultiArray {
435 handles,
436 sizes,
437 session: self.0.node.session.clone(),
438 },
439 ))
440 }
441 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
442 debug_assert!(self.0.node.is_valid()?);
443 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
444 bindings::set_attribute_string_array_data(
445 &self.0.node,
446 self.0.name.as_c_str(),
447 &self.0.info.0,
448 part_id,
449 ptrs.as_mut(),
450 sizes,
451 )
452 }
453
454 pub fn set_async(
455 &self,
456 part_id: i32,
457 values: &[impl AsRef<CStr>],
458 sizes: &[i32],
459 ) -> Result<JobId> {
460 debug_assert!(self.0.node.is_valid()?);
461 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
462 bindings::set_attribute_string_array_data_async(
463 &self.0.node,
464 self.0.name.as_c_str(),
465 part_id,
466 &self.0.info.0,
467 ptrs.as_mut(),
468 sizes,
469 )
470 }
471}
472
473impl DictionaryAttr {
474 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
475 DictionaryAttr(AttributeBundle { info, name, node })
476 }
477
478 pub fn get(&self, part_id: i32) -> Result<StringArray> {
479 debug_assert!(self.0.node.is_valid()?);
480 bindings::get_attribute_dictionary_data(
481 &self.0.node,
482 part_id,
483 self.0.name.as_c_str(),
484 &self.0.info.0,
485 )
486 }
487
488 pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
489 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
490 bindings::set_attribute_dictionary_data_async(
491 &self.0.node,
492 self.0.name.as_c_str(),
493 part_id,
494 &self.0.info.0,
495 ptrs.as_mut(),
496 )
497 }
498
499 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
500 bindings::get_attribute_dictionary_data_async(
501 &self.0.node,
502 part_id,
503 self.0.name.as_c_str(),
504 &self.0.info.0,
505 )
506 }
507
508 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
510 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
511 bindings::set_attribute_dictionary_data(
512 &self.0.node,
513 part_id,
514 self.0.name.as_c_str(),
515 &self.0.info.0,
516 ptrs.as_mut(),
517 )
518 }
519}
520
521impl DictionaryArrayAttr {
522 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
523 DictionaryArrayAttr(AttributeBundle { info, name, node })
524 }
525 pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
526 debug_assert!(self.0.node.is_valid()?);
527 bindings::get_attribute_dictionary_array_data(
528 &self.0.node,
529 &self.0.name,
530 part_id,
531 &self.0.info,
532 )
533 }
534
535 pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
536 let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
537 let mut sizes = vec![0; self.0.info.count() as usize];
538 let job_id = bindings::get_attribute_dictionary_array_data_async(
539 &self.0.node,
540 self.0.name.as_c_str(),
541 part_id,
542 &self.0.info.0,
543 &mut handles,
544 &mut sizes,
545 )?;
546 Ok((
547 job_id,
548 StringMultiArray {
549 handles,
550 sizes,
551 session: self.0.node.session.clone(),
552 },
553 ))
554 }
555 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
556 debug_assert!(self.0.node.is_valid()?);
557 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
558 bindings::set_attribute_dictionary_array_data(
559 &self.0.node,
560 self.0.name.as_c_str(),
561 &self.0.info.0,
562 part_id,
563 ptrs.as_mut(),
564 sizes,
565 )
566 }
567
568 pub fn set_async(
569 &self,
570 part_id: i32,
571 values: &[impl AsRef<CStr>],
572 sizes: &[i32],
573 ) -> Result<JobId> {
574 debug_assert!(self.0.node.is_valid()?);
575 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
576 bindings::set_attribute_dictionary_array_data_async(
577 &self.0.node,
578 self.0.name.as_c_str(),
579 part_id,
580 &self.0.info.0,
581 ptrs.as_mut(),
582 sizes,
583 )
584 }
585}
586
587#[doc(hidden)]
588pub trait AsAttribute: Send {
589 fn info(&self) -> &AttributeInfo;
590 fn storage(&self) -> StorageType;
591 fn boxed(self) -> Box<dyn AnyAttribWrapper>
592 where
593 Self: Sized + 'static,
594 {
595 Box::new(self)
596 }
597 fn name(&self) -> &CStr;
598 fn node(&self) -> &HoudiniNode;
599}
600
601impl<T: AttribAccess> AsAttribute for NumericAttr<T> {
602 fn info(&self) -> &AttributeInfo {
603 &self.0.info
604 }
605 fn storage(&self) -> StorageType {
606 T::storage()
607 }
608
609 fn name(&self) -> &CStr {
610 &self.0.name
611 }
612
613 fn node(&self) -> &HoudiniNode {
614 &self.0.node
615 }
616}
617
618impl<T: AttribAccess> AsAttribute for NumericArrayAttr<T> {
619 fn info(&self) -> &AttributeInfo {
620 &self.0.info
621 }
622 fn storage(&self) -> StorageType {
623 T::storage()
624 }
625 fn name(&self) -> &CStr {
626 &self.0.name
627 }
628 fn node(&self) -> &HoudiniNode {
629 &self.0.node
630 }
631}
632
633impl AsAttribute for StringAttr {
634 fn info(&self) -> &AttributeInfo {
635 &self.0.info
636 }
637
638 fn storage(&self) -> StorageType {
639 StorageType::String
640 }
641
642 fn name(&self) -> &CStr {
643 &self.0.name
644 }
645
646 fn node(&self) -> &HoudiniNode {
647 &self.0.node
648 }
649}
650
651impl AsAttribute for StringArrayAttr {
652 fn info(&self) -> &AttributeInfo {
653 &self.0.info
654 }
655
656 fn storage(&self) -> StorageType {
657 StorageType::StringArray
658 }
659
660 fn name(&self) -> &CStr {
661 &self.0.name
662 }
663
664 fn node(&self) -> &HoudiniNode {
665 &self.0.node
666 }
667}
668
669impl AsAttribute for DictionaryAttr {
670 fn info(&self) -> &AttributeInfo {
671 &self.0.info
672 }
673
674 fn storage(&self) -> StorageType {
675 StorageType::Dictionary
676 }
677
678 fn name(&self) -> &CStr {
679 &self.0.name
680 }
681
682 fn node(&self) -> &HoudiniNode {
683 &self.0.node
684 }
685}
686
687impl AsAttribute for DictionaryArrayAttr {
688 fn info(&self) -> &AttributeInfo {
689 &self.0.info
690 }
691
692 fn storage(&self) -> StorageType {
693 StorageType::DictionaryArray
694 }
695
696 fn name(&self) -> &CStr {
697 &self.0.name
698 }
699
700 fn node(&self) -> &HoudiniNode {
701 &self.0.node
702 }
703}
704
705#[doc(hidden)]
706pub trait AnyAttribWrapper: Any + AsAttribute {
707 fn as_any(&self) -> &dyn Any;
708}
709
710impl<T: AsAttribute + 'static> AnyAttribWrapper for T {
711 fn as_any(&self) -> &dyn Any {
712 self
713 }
714}
715
716pub struct Attribute(Box<dyn AnyAttribWrapper>);
717
718impl Attribute {
719 pub(crate) fn new(attr_obj: Box<dyn AnyAttribWrapper>) -> Self {
720 Attribute(attr_obj)
721 }
722 pub fn downcast<T: AnyAttribWrapper>(&self) -> Option<&T> {
723 self.0.as_any().downcast_ref::<T>()
724 }
725 pub fn name(&self) -> Cow<str> {
726 self.0.name().to_string_lossy()
727 }
728 pub fn storage(&self) -> StorageType {
729 self.0.storage()
730 }
731 pub fn info(&self) -> &AttributeInfo {
732 self.0.info()
733 }
734 pub fn delete(self, part_id: i32) -> Result<()> {
735 crate::ffi::delete_attribute(self.0.node(), part_id, self.0.name(), &self.0.info().0)
736 }
737}