1mod array;
22mod async_;
23mod bindings;
24
25use crate::errors::{ErrorContext, Result};
26pub use crate::ffi::AttributeInfo;
27pub use crate::ffi::enums::StorageType;
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, info, part_id, buffer).with_context(|| {
259 format!(
260 "Reading attribute \"{}\" asynchronously, into buffer size {}",
261 self.0.name.to_string_lossy(),
262 buffer.len()
263 )
264 })
265 }
266
267 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<T>> {
268 let info = &self.0.info;
269 let size = (info.count() * info.tuple_size()) as usize;
270 let mut data = Vec::<T>::with_capacity(size);
271 let job_id = T::get_async(&self.0.name, &self.0.node, info, part_id, &mut data)
272 .with_context(|| {
273 format!(
274 "Reading attribute {} asynchronously",
275 self.0.name.to_string_lossy()
276 )
277 })?;
278 Ok(AsyncAttribResult {
279 job_id,
280 data,
281 size,
282 session: self.0.node.session.clone(),
283 })
284 }
285
286 pub fn read_into(&self, part_id: i32, buffer: &mut Vec<T>) -> Result<()> {
289 debug_assert_eq!(self.0.info.storage(), T::storage());
290 let info = AttributeInfo::new(&self.0.node, part_id, self.0.info.owner(), &self.0.name)?;
292 T::get(&self.0.name, &self.0.node, &info, part_id, buffer)
293 }
294 pub fn set(&self, part_id: i32, values: &[T]) -> Result<()> {
295 debug_assert_eq!(self.0.info.storage(), T::storage());
296 T::set(
297 &self.0.name,
298 &self.0.node,
299 &self.0.info,
300 part_id,
301 values,
302 0,
303 self.0.info.count().min(values.len() as i32),
304 )
305 }
306
307 pub fn set_unique(&self, part_id: i32, value: &[T]) -> Result<()> {
310 debug_assert_eq!(self.0.info.storage(), T::storage());
311 debug_assert!(value.len() <= self.0.info.tuple_size() as usize);
312 T::set_unique(&self.0.name, &self.0.node, &self.0.info, part_id, value, 0)
313 }
314}
315
316impl StringAttr {
317 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringAttr {
318 StringAttr(AttributeBundle { info, name, node })
319 }
320 pub fn get(&self, part_id: i32) -> Result<StringArray> {
321 debug_assert!(self.0.node.is_valid()?);
322 bindings::get_attribute_string_data(
323 &self.0.node,
324 part_id,
325 self.0.name.as_c_str(),
326 &self.0.info.0,
327 )
328 }
329
330 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
331 bindings::get_attribute_string_data_async(
332 &self.0.node,
333 part_id,
334 self.0.name.as_c_str(),
335 &self.0.info.0,
336 )
337 }
338
339 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
340 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
341 bindings::set_attribute_string_data(
342 &self.0.node,
343 part_id,
344 self.0.name.as_c_str(),
345 &self.0.info.0,
346 ptrs.as_mut(),
347 )
348 }
349
350 pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
351 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
352 bindings::set_attribute_string_data_async(
353 &self.0.node,
354 self.0.name.as_c_str(),
355 part_id,
356 &self.0.info.0,
357 ptrs.as_mut(),
358 )
359 }
360 pub fn set_unique(&self, part: i32, value: &CStr) -> Result<()> {
363 bindings::set_attribute_string_unique_data(
364 &self.0.node,
365 self.0.name.as_c_str(),
366 &self.0.info.0,
367 part,
368 value.as_ptr(),
369 )
370 }
371
372 pub fn set_unique_async(&self, part: i32, value: &CStr) -> Result<JobId> {
374 bindings::set_attribute_string_unique_data_async(
375 &self.0.node,
376 self.0.name.as_c_str(),
377 &self.0.info.0,
378 part,
379 value.as_ptr(),
380 )
381 }
382
383 pub fn set_indexed(
385 &self,
386 part_id: i32,
387 values: &[impl AsRef<CStr>],
388 indices: &[i32],
389 ) -> Result<()> {
390 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
391 bindings::set_attribute_indexed_string_data(
392 &self.0.node,
393 part_id,
394 self.0.name.as_c_str(),
395 &self.0.info.0,
396 ptrs.as_mut(),
397 indices,
398 )
399 }
400
401 pub fn set_indexed_async(
402 &self,
403 part_id: i32,
404 values: &[impl AsRef<CStr>],
405 indices: &[i32],
406 ) -> Result<JobId> {
407 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
408 bindings::set_attribute_indexed_string_data_async(
409 &self.0.node,
410 part_id,
411 self.0.name.as_c_str(),
412 &self.0.info.0,
413 ptrs.as_mut(),
414 indices,
415 )
416 }
417}
418
419impl StringArrayAttr {
420 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> StringArrayAttr {
421 StringArrayAttr(AttributeBundle { info, name, node })
422 }
423 pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
424 debug_assert!(self.0.node.is_valid()?);
425 bindings::get_attribute_string_array_data(
426 &self.0.node,
427 self.0.name.as_c_str(),
428 part_id,
429 &self.0.info,
430 )
431 }
432
433 pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
434 let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
435 let mut sizes = vec![0; self.0.info.count() as usize];
436 let job_id = bindings::get_attribute_string_array_data_async(
437 &self.0.node,
438 self.0.name.as_c_str(),
439 part_id,
440 &self.0.info.0,
441 &mut handles,
442 &mut sizes,
443 )?;
444 Ok((
445 job_id,
446 StringMultiArray {
447 handles,
448 sizes,
449 session: self.0.node.session.clone(),
450 },
451 ))
452 }
453 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
454 debug_assert!(self.0.node.is_valid()?);
455 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
456 bindings::set_attribute_string_array_data(
457 &self.0.node,
458 self.0.name.as_c_str(),
459 &self.0.info.0,
460 part_id,
461 ptrs.as_mut(),
462 sizes,
463 )
464 }
465
466 pub fn set_async(
467 &self,
468 part_id: i32,
469 values: &[impl AsRef<CStr>],
470 sizes: &[i32],
471 ) -> Result<JobId> {
472 debug_assert!(self.0.node.is_valid()?);
473 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
474 bindings::set_attribute_string_array_data_async(
475 &self.0.node,
476 self.0.name.as_c_str(),
477 part_id,
478 &self.0.info.0,
479 ptrs.as_mut(),
480 sizes,
481 )
482 }
483}
484
485impl DictionaryAttr {
486 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
487 DictionaryAttr(AttributeBundle { info, name, node })
488 }
489
490 pub fn get(&self, part_id: i32) -> Result<StringArray> {
491 debug_assert!(self.0.node.is_valid()?);
492 bindings::get_attribute_dictionary_data(
493 &self.0.node,
494 part_id,
495 self.0.name.as_c_str(),
496 &self.0.info.0,
497 )
498 }
499
500 pub fn set_async(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<JobId> {
501 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
502 bindings::set_attribute_dictionary_data_async(
503 &self.0.node,
504 self.0.name.as_c_str(),
505 part_id,
506 &self.0.info.0,
507 ptrs.as_mut(),
508 )
509 }
510
511 pub fn get_async(&self, part_id: i32) -> Result<AsyncAttribResult<StringHandle>> {
512 bindings::get_attribute_dictionary_data_async(
513 &self.0.node,
514 part_id,
515 self.0.name.as_c_str(),
516 &self.0.info.0,
517 )
518 }
519
520 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>]) -> Result<()> {
522 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
523 bindings::set_attribute_dictionary_data(
524 &self.0.node,
525 part_id,
526 self.0.name.as_c_str(),
527 &self.0.info.0,
528 ptrs.as_mut(),
529 )
530 }
531}
532
533impl DictionaryArrayAttr {
534 pub(crate) fn new(name: CString, info: AttributeInfo, node: HoudiniNode) -> Self {
535 DictionaryArrayAttr(AttributeBundle { info, name, node })
536 }
537 pub fn get(&self, part_id: i32) -> Result<StringMultiArray> {
538 debug_assert!(self.0.node.is_valid()?);
539 bindings::get_attribute_dictionary_array_data(
540 &self.0.node,
541 &self.0.name,
542 part_id,
543 &self.0.info,
544 )
545 }
546
547 pub fn get_async(&self, part_id: i32) -> Result<(JobId, StringMultiArray)> {
548 let mut handles = vec![StringHandle(-1); self.0.info.total_array_elements() as usize];
549 let mut sizes = vec![0; self.0.info.count() as usize];
550 let job_id = bindings::get_attribute_dictionary_array_data_async(
551 &self.0.node,
552 self.0.name.as_c_str(),
553 part_id,
554 &self.0.info.0,
555 &mut handles,
556 &mut sizes,
557 )?;
558 Ok((
559 job_id,
560 StringMultiArray {
561 handles,
562 sizes,
563 session: self.0.node.session.clone(),
564 },
565 ))
566 }
567 pub fn set(&self, part_id: i32, values: &[impl AsRef<CStr>], sizes: &[i32]) -> Result<()> {
568 debug_assert!(self.0.node.is_valid()?);
569 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
570 bindings::set_attribute_dictionary_array_data(
571 &self.0.node,
572 self.0.name.as_c_str(),
573 &self.0.info.0,
574 part_id,
575 ptrs.as_mut(),
576 sizes,
577 )
578 }
579
580 pub fn set_async(
581 &self,
582 part_id: i32,
583 values: &[impl AsRef<CStr>],
584 sizes: &[i32],
585 ) -> Result<JobId> {
586 debug_assert!(self.0.node.is_valid()?);
587 let mut ptrs: Vec<*const i8> = values.iter().map(|cs| cs.as_ref().as_ptr()).collect();
588 bindings::set_attribute_dictionary_array_data_async(
589 &self.0.node,
590 self.0.name.as_c_str(),
591 part_id,
592 &self.0.info.0,
593 ptrs.as_mut(),
594 sizes,
595 )
596 }
597}
598
599#[doc(hidden)]
600pub trait AsAttribute: Send {
601 fn info(&self) -> &AttributeInfo;
602 fn storage(&self) -> StorageType;
603 fn boxed(self) -> Box<dyn AnyAttribWrapper>
604 where
605 Self: Sized + 'static,
606 {
607 Box::new(self)
608 }
609 fn name(&self) -> &CStr;
610 fn node(&self) -> &HoudiniNode;
611}
612
613impl<T: AttribAccess> AsAttribute for NumericAttr<T> {
614 fn info(&self) -> &AttributeInfo {
615 &self.0.info
616 }
617 fn storage(&self) -> StorageType {
618 T::storage()
619 }
620
621 fn name(&self) -> &CStr {
622 &self.0.name
623 }
624
625 fn node(&self) -> &HoudiniNode {
626 &self.0.node
627 }
628}
629
630impl<T: AttribAccess> AsAttribute for NumericArrayAttr<T> {
631 fn info(&self) -> &AttributeInfo {
632 &self.0.info
633 }
634 fn storage(&self) -> StorageType {
635 T::storage()
636 }
637 fn name(&self) -> &CStr {
638 &self.0.name
639 }
640 fn node(&self) -> &HoudiniNode {
641 &self.0.node
642 }
643}
644
645impl AsAttribute for StringAttr {
646 fn info(&self) -> &AttributeInfo {
647 &self.0.info
648 }
649
650 fn storage(&self) -> StorageType {
651 StorageType::String
652 }
653
654 fn name(&self) -> &CStr {
655 &self.0.name
656 }
657
658 fn node(&self) -> &HoudiniNode {
659 &self.0.node
660 }
661}
662
663impl AsAttribute for StringArrayAttr {
664 fn info(&self) -> &AttributeInfo {
665 &self.0.info
666 }
667
668 fn storage(&self) -> StorageType {
669 StorageType::StringArray
670 }
671
672 fn name(&self) -> &CStr {
673 &self.0.name
674 }
675
676 fn node(&self) -> &HoudiniNode {
677 &self.0.node
678 }
679}
680
681impl AsAttribute for DictionaryAttr {
682 fn info(&self) -> &AttributeInfo {
683 &self.0.info
684 }
685
686 fn storage(&self) -> StorageType {
687 StorageType::Dictionary
688 }
689
690 fn name(&self) -> &CStr {
691 &self.0.name
692 }
693
694 fn node(&self) -> &HoudiniNode {
695 &self.0.node
696 }
697}
698
699impl AsAttribute for DictionaryArrayAttr {
700 fn info(&self) -> &AttributeInfo {
701 &self.0.info
702 }
703
704 fn storage(&self) -> StorageType {
705 StorageType::DictionaryArray
706 }
707
708 fn name(&self) -> &CStr {
709 &self.0.name
710 }
711
712 fn node(&self) -> &HoudiniNode {
713 &self.0.node
714 }
715}
716
717#[doc(hidden)]
718pub trait AnyAttribWrapper: Any + AsAttribute {
719 fn as_any(&self) -> &dyn Any;
720}
721
722impl<T: AsAttribute + 'static> AnyAttribWrapper for T {
723 fn as_any(&self) -> &dyn Any {
724 self
725 }
726}
727
728pub struct Attribute(Box<dyn AnyAttribWrapper>);
729
730impl Attribute {
731 pub(crate) fn new(attr_obj: Box<dyn AnyAttribWrapper>) -> Self {
732 Attribute(attr_obj)
733 }
734 pub fn downcast<T: AnyAttribWrapper>(&self) -> Option<&T> {
735 self.0.as_any().downcast_ref::<T>()
736 }
737 pub fn name(&self) -> Cow<'_, str> {
738 self.0.name().to_string_lossy()
739 }
740 pub fn storage(&self) -> StorageType {
741 self.0.storage()
742 }
743 pub fn info(&self) -> &AttributeInfo {
744 self.0.info()
745 }
746 pub fn delete(self, part_id: i32) -> Result<()> {
747 crate::ffi::delete_attribute(self.0.node(), part_id, self.0.name(), &self.0.info().0)
748 }
749}