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