hapi_rs/
node.rs

1//! Manipulating Houdini nodes and networks, getting geometry and parameters
2//!
3//! Any Houdini nodes is represented as [`HoudiniNode`] struct and all node-related functions are exposed as
4//! methods on that struct. It has a public `info` filed with [`NodeInfo`] with details about the node.
5//!
6//! Nodes can be created with [`Session::create_node`]
7//!
8//! HoudiniNode is ['Clone'], [`Sync`] and [`Send`]
9use std::path::Path;
10use std::str::FromStr;
11use std::sync::Arc;
12use std::{ffi::CString, ffi::OsStr, fmt::Formatter};
13
14use log::debug;
15
16use crate::pdg::TopNode;
17pub use crate::{
18    errors::Result,
19    ffi::{AssetInfo, GeoInfo, KeyFrame, NodeInfo, ObjectInfo, ParmInfo},
20    geometry::Geometry,
21    parameter::*,
22    session::{CookResult, Session},
23};
24pub use crate::{
25    ffi::raw::{
26        ErrorCode, NodeFlags, NodeType, PresetType, RSTOrder, State, StatusType, StatusVerbosity,
27        TransformComponent,
28    },
29    ffi::{CookOptions, Transform, TransformEuler},
30};
31
32impl std::fmt::Display for NodeType {
33    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34        f.write_str(match *self {
35            NodeType::Sop => "Sop",
36            NodeType::Obj => "Obj",
37            NodeType::Rop => "Rop",
38            NodeType::Dop => "Dop",
39            NodeType::Cop => "Cop",
40            NodeType::Shop => "Shop",
41            NodeType::Vop => "Vop",
42            NodeType::Chop => "Chop",
43            _ => "Unknown",
44        })
45    }
46}
47
48/// Types of Houdini manager nodes (contexts).
49#[derive(Debug, Copy, Clone)]
50#[non_exhaustive]
51pub enum ManagerType {
52    Obj,
53    Chop,
54    Cop,
55    Rop,
56    Top,
57}
58
59impl FromStr for ManagerType {
60    type Err = crate::HapiError;
61
62    fn from_str(val: &str) -> Result<Self> {
63        match val {
64            "Cop2" => Ok(Self::Cop),
65            "Chop" => Ok(Self::Chop),
66            "Top" => Ok(Self::Top),
67            "Object" => Ok(Self::Obj),
68            "Driver" => Ok(Self::Rop),
69            v => Err(crate::HapiError::internal(format!(
70                "Unknown ManagerType::{v}"
71            ))),
72        }
73    }
74}
75
76impl From<ManagerType> for NodeType {
77    fn from(value: ManagerType) -> Self {
78        use ManagerType::*;
79        match value {
80            Obj => NodeType::Obj,
81            Chop => NodeType::Chop,
82            Cop => NodeType::Cop,
83            Rop => NodeType::Rop,
84            Top => NodeType::Top,
85        }
86    }
87}
88
89// Helper function to return all child nodes of specified type
90fn get_child_node_list(
91    session: &Session,
92    parent: impl Into<NodeHandle>,
93    types: NodeType,
94    flags: NodeFlags,
95    recursive: bool,
96) -> Result<Vec<NodeHandle>> {
97    let ids =
98        crate::ffi::get_compose_child_node_list(session, parent.into(), types, flags, recursive)?;
99    Ok(ids.into_iter().map(NodeHandle).collect())
100}
101
102// Helper function to return all network type nodes.
103fn find_networks_nodes(
104    session: &Session,
105    types: NodeType,
106    parent: impl Into<NodeHandle>,
107    recursive: bool,
108) -> Result<Vec<HoudiniNode>> {
109    debug_assert!(session.is_valid());
110    get_child_node_list(session, parent, types, NodeFlags::Network, recursive).map(|vec| {
111        vec.into_iter()
112            .map(|handle| handle.to_node(session))
113            .collect::<Result<Vec<_>>>()
114    })?
115}
116
117#[derive(Debug, Clone)]
118/// Represents a manager node (OBJ, SOP, etc)
119pub struct ManagerNode {
120    pub session: Session,
121    pub handle: NodeHandle,
122    pub node_type: ManagerType,
123}
124
125impl ManagerNode {
126    /// Find network nodes of given type.
127    pub fn find_network_nodes(&self, types: NodeType) -> Result<Vec<HoudiniNode>> {
128        find_networks_nodes(&self.session, types, self.handle, true)
129    }
130
131    /// Return children nodes of this network.
132    pub fn get_children(&self) -> Result<Vec<NodeHandle>> {
133        get_child_node_list(
134            &self.session,
135            self.handle,
136            NodeType::from(self.node_type),
137            NodeFlags::Any,
138            false,
139        )
140    }
141}
142
143#[repr(transparent)]
144#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
145/// A lightweight handle to a node. Can not be created manually, use [`HoudiniNode`] instead.
146/// Some APIs return a list of such handles for efficiency, for example [`HoudiniNode::find_children_by_type`].
147/// Once you found the node you're looking for, upgrade it to a "full" node type.
148pub struct NodeHandle(pub(crate) crate::ffi::raw::HAPI_NodeId);
149
150impl From<NodeHandle> for crate::ffi::raw::HAPI_NodeId {
151    fn from(h: NodeHandle) -> Self {
152        h.0
153    }
154}
155
156impl AsRef<NodeHandle> for HoudiniNode {
157    fn as_ref(&self) -> &NodeHandle {
158        &self.handle
159    }
160}
161
162impl AsRef<NodeHandle> for NodeHandle {
163    fn as_ref(&self) -> &NodeHandle {
164        self
165    }
166}
167
168impl Default for NodeHandle {
169    fn default() -> Self {
170        NodeHandle(-1)
171    }
172}
173
174impl NodeHandle {
175    /// Retrieve info about the node this handle belongs to
176    pub fn info(&self, session: &Session) -> Result<NodeInfo> {
177        NodeInfo::new(session, *self)
178    }
179
180    /// Returns node's internal path.
181    pub fn path(&self, session: &Session) -> Result<String> {
182        debug_assert!(self.is_valid(session)?, "Invalid {:?}", self);
183        crate::ffi::get_node_path(session, *self, None)
184    }
185
186    /// Returns node's path relative to another node.
187    pub fn path_relative(
188        &self,
189        session: &Session,
190        to: impl Into<Option<NodeHandle>>,
191    ) -> Result<String> {
192        debug_assert!(self.is_valid(session)?, "Invalid {:?}", self);
193        crate::ffi::get_node_path(session, *self, to.into())
194    }
195
196    /// Check if the handle is valid (node wasn't deleted)
197    pub fn is_valid(&self, session: &Session) -> Result<bool> {
198        let info = self.info(session)?;
199        crate::ffi::is_node_valid(session, &info.0)
200    }
201
202    /// Upgrade the handle to HoudiniNode, which has more capabilities.
203    pub fn to_node(&self, session: &Session) -> Result<HoudiniNode> {
204        HoudiniNode::new(session.clone(), *self, None)
205    }
206
207    /// Upgrade the handle to Geometry node.
208    pub fn as_geometry_node(&self, session: &Session) -> Result<Option<Geometry>> {
209        let info = NodeInfo::new(session, *self)?;
210        match info.node_type() {
211            NodeType::Sop => Ok(Some(Geometry {
212                node: HoudiniNode::new(session.clone(), *self, Some(info))?,
213                info: GeoInfo::from_handle(*self, session)?,
214            })),
215            _ => Ok(None),
216        }
217    }
218
219    /// If this is a handle to a TOP node, returns a [`TopNode`] type.
220    pub fn as_top_node(&self, session: &Session) -> Result<Option<TopNode>> {
221        let node = self.to_node(session)?;
222        match node.info.node_type() {
223            NodeType::Top => Ok(Some(TopNode { node })),
224            _ => Ok(None),
225        }
226    }
227}
228
229#[derive(Clone)]
230/// Represents a Houdini node
231pub struct HoudiniNode {
232    pub handle: NodeHandle,
233    pub session: Session,
234    pub info: Arc<NodeInfo>,
235}
236
237impl PartialEq for HoudiniNode {
238    fn eq(&self, other: &Self) -> bool {
239        self.handle == other.handle && self.session == other.session
240    }
241}
242
243impl std::fmt::Debug for HoudiniNode {
244    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
245        f.debug_struct("HoudiniNode")
246            .field("id", &self.handle.0)
247            .field("type", &self.info.node_type())
248            .field(
249                "path",
250                &self.path().expect("[HoudiniNode::Debug] node path"),
251            )
252            .finish()
253    }
254}
255
256impl From<HoudiniNode> for NodeHandle {
257    fn from(n: HoudiniNode) -> Self {
258        n.handle
259    }
260}
261
262impl From<HoudiniNode> for Option<NodeHandle> {
263    fn from(n: HoudiniNode) -> Self {
264        Some(n.handle)
265    }
266}
267
268impl From<&HoudiniNode> for Option<NodeHandle> {
269    fn from(n: &HoudiniNode) -> Self {
270        Some(n.handle)
271    }
272}
273
274impl From<&HoudiniNode> for NodeHandle {
275    fn from(n: &HoudiniNode) -> Self {
276        n.handle
277    }
278}
279
280impl HoudiniNode {
281    pub(crate) fn new(
282        session: Session,
283        handle: NodeHandle,
284        info: Option<NodeInfo>,
285    ) -> Result<Self> {
286        let info = Arc::new(match info {
287            None => NodeInfo::new(&session, handle)?,
288            Some(i) => i,
289        });
290        Ok(HoudiniNode {
291            handle,
292            session,
293            info,
294        })
295    }
296
297    /// Convert this node instance into [`TopNode`]
298    pub fn to_top_node(self) -> Option<TopNode> {
299        match self.info.node_type() {
300            NodeType::Top => Some(TopNode { node: self }),
301            _ => None,
302        }
303    }
304    /// Delete the node in this session.
305    pub fn delete(self) -> Result<()> {
306        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
307        crate::ffi::delete_node(self.handle, &self.session)
308    }
309
310    /// Checks if the node valid (not deleted).
311    pub fn is_valid(&self) -> Result<bool> {
312        self.handle.is_valid(&self.session)
313    }
314
315    pub fn name(&self) -> Result<String> {
316        self.info.name()
317    }
318
319    /// Returns node's internal path.
320    pub fn path(&self) -> Result<String> {
321        self.handle.path(&self.session)
322    }
323
324    /// Returns node's path relative to another node.
325    pub fn path_relative(&self, to: impl Into<Option<NodeHandle>>) -> Result<String> {
326        self.handle.path_relative(&self.session, to)
327    }
328
329    /// Start cooking the node. This is a non-blocking call if the session is async.
330    pub fn cook(&self) -> Result<()> {
331        debug!("Start cooking node: {}", self.path()?);
332        debug_assert!(self.is_valid()?);
333        crate::ffi::cook_node(self, &CookOptions::default())
334    }
335
336    /// Start cooking the node and wait until completed.
337    /// In sync mode (single threaded), the error will be available in Err(..) while
338    /// in threaded cooking mode the status will be in [`CookResult`]
339    pub fn cook_blocking(&self) -> Result<CookResult> {
340        debug!("Start cooking node: {}", self.path()?);
341        debug_assert!(self.is_valid()?);
342        crate::ffi::cook_node(self, &CookOptions::default())?;
343        self.session.cook()
344    }
345
346    /// Start cooking with options and wait for result if blocking = true.
347    pub fn cook_with_options(&self, options: &CookOptions, blocking: bool) -> Result<CookResult> {
348        debug!("Start cooking node: {}", self.path()?);
349        debug_assert!(self.is_valid()?);
350        crate::ffi::cook_node(self, options)?;
351        if blocking {
352            self.session.cook()
353        } else {
354            Ok(CookResult::Succeeded)
355        }
356    }
357
358    /// How many times this node has been cooked.
359    pub fn cook_count(
360        &self,
361        node_types: NodeType,
362        node_flags: NodeFlags,
363        recurse: bool,
364    ) -> Result<i32> {
365        debug_assert!(self.is_valid()?);
366        crate::ffi::get_total_cook_count(self, node_types, node_flags, recurse)
367    }
368
369    /// If the node is of Object type, get the information object about it.
370    pub fn get_object_info(&self) -> Result<ObjectInfo<'_>> {
371        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
372        crate::ffi::get_object_info(&self.session, self.handle)
373            .map(|info| ObjectInfo(info, (&self.session).into()))
374    }
375
376    /// Get a new NodeInfo even for this node.
377    pub fn get_info(&self) -> Result<NodeInfo> {
378        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
379        self.handle.info(&self.session)
380    }
381
382    /// Returns information objects about this node children.
383    pub fn get_objects_info(&self) -> Result<Vec<ObjectInfo<'_>>> {
384        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
385        let parent = match self.info.node_type() {
386            NodeType::Obj => self.info.parent_id(),
387            _ => self.handle,
388        };
389        let infos = crate::ffi::get_composed_object_list(&self.session, parent)?;
390        Ok(infos
391            .into_iter()
392            .map(|inner| ObjectInfo(inner, (&self.session).into()))
393            .collect())
394    }
395
396    /// Find all children of this node by type.
397    pub fn find_children_by_type(
398        &self,
399        types: NodeType,
400        flags: NodeFlags,
401        recursive: bool,
402    ) -> Result<Vec<NodeHandle>> {
403        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
404        get_child_node_list(&self.session, self, types, flags, recursive)
405    }
406
407    /// Get all children of the node, not recursively.
408    pub fn get_children(&self) -> Result<Vec<NodeHandle>> {
409        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
410        get_child_node_list(&self.session, self, NodeType::Any, NodeFlags::Any, false)
411    }
412
413    /// Get a child node by path.
414    pub fn get_child_by_path(&self, relative_path: &str) -> Result<Option<HoudiniNode>> {
415        self.session
416            .get_node_from_path(relative_path, Some(self.handle))
417    }
418
419    /// Get the node ids for the objects being instanced by an Instance OBJ node.
420    pub fn get_instanced_object_ids(&self) -> Result<Vec<NodeHandle>> {
421        crate::ffi::get_instanced_object_ids(self)
422    }
423
424    /// *Search* for child node by name.
425    pub fn find_child_node(
426        &self,
427        name: impl AsRef<str>,
428        recursive: bool,
429    ) -> Result<Option<HoudiniNode>> {
430        debug_assert!(self.is_valid()?);
431        if !recursive {
432            return self.get_child_by_path(name.as_ref());
433        }
434        for handle in self.find_children_by_type(NodeType::Any, NodeFlags::Any, recursive)? {
435            let info = handle.info(&self.session)?;
436            if info.name()? == name.as_ref() {
437                return Ok(Some(HoudiniNode::new(
438                    self.session.clone(),
439                    handle,
440                    Some(info),
441                )?));
442            }
443        }
444        Ok(None)
445    }
446
447    /// Given if Self is an asset or a subnet SOP node, get its output node at index.
448    pub fn get_sop_output_node(&self, index: i32) -> Result<NodeHandle> {
449        debug_assert!(self.is_valid()?);
450        crate::ffi::get_sop_output_node(&self.session, self.handle, index)
451    }
452
453    /// Return the node's parent.
454    pub fn parent_node(&self) -> Option<NodeHandle> {
455        let handle = self.info.parent_id();
456        (handle.0 > -1).then_some(handle)
457    }
458
459    /// Find a parameter on the node by name. Err() means parameter not found.
460    pub fn parameter(&self, name: &str) -> Result<Parameter> {
461        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
462        let parm_info = ParmInfo::from_parm_name(name, self)?;
463        Ok(Parameter::new(self.handle, parm_info))
464    }
465
466    /// Find a parameter with a specific tag
467    pub fn parameter_with_tag(&self, tag: &str) -> Result<Option<Parameter>> {
468        let tag = CString::new(tag)?;
469        match crate::ffi::get_parm_with_tag(self, &tag)? {
470            -1 => Ok(None),
471            h => {
472                let parm_info =
473                    ParmInfo::from_parm_handle(ParmHandle(h), self.handle, &self.session)?;
474                Ok(Some(Parameter::new(self.handle, parm_info)))
475            }
476        }
477    }
478
479    /// Return all node parameters.
480    pub fn parameters(&self) -> Result<Vec<Parameter>> {
481        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
482        let infos = crate::ffi::get_parameters(self)?;
483        Ok(infos
484            .into_iter()
485            .map(|info| {
486                Parameter::new(self.handle, ParmInfo::new(info, self.session.clone(), None))
487            })
488            .collect())
489    }
490
491    /// If node is an HDA, return [`AssetInfo'] about it.
492    pub fn asset_info(&self) -> Result<AssetInfo> {
493        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
494        Ok(AssetInfo(
495            crate::ffi::get_asset_info(self)?,
496            self.session.clone().into(),
497        ))
498    }
499    /// Recursively check all nodes for a specific error.
500    pub fn check_for_specific_error(&self, error_bits: i32) -> Result<ErrorCode> {
501        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
502        crate::ffi::check_for_specific_errors(self, error_bits)
503    }
504
505    /// Compose the cook result (errors and warnings) of all nodes in the network into a string.
506    pub fn get_composed_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
507        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
508        unsafe { crate::ffi::get_composed_cook_result(self, verbosity) }
509    }
510
511    /// Get the cook errors and warnings on this node as a string
512    pub fn get_cook_result_string(&self, verbosity: StatusVerbosity) -> Result<String> {
513        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
514        let bytes = crate::ffi::get_node_cook_result(self, verbosity)?;
515        Ok(String::from_utf8_lossy(&bytes).to_string())
516    }
517    /// Resets the simulation cache of the asset.
518    pub fn reset_simulation(&self) -> Result<()> {
519        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
520        crate::ffi::reset_simulation(self)
521    }
522
523    /// Return a node connected to given input.
524    pub fn input_node(&self, idx: i32) -> Result<Option<HoudiniNode>> {
525        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
526        crate::ffi::query_node_input(self, idx).map(|idx| {
527            if idx == -1 {
528                None
529            } else {
530                HoudiniNode::new(self.session.clone(), NodeHandle(idx), None).ok()
531            }
532        })
533    }
534
535    /// Give the node a new name.
536    pub fn rename(&self, new_name: impl AsRef<str>) -> Result<()> {
537        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
538        let name = CString::new(new_name.as_ref())?;
539        crate::ffi::rename_node(self, &name)
540    }
541
542    /// Saves the node and all its contents to file
543    pub fn save_to_file(&self, file: impl AsRef<Path>) -> Result<()> {
544        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
545        let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
546        crate::ffi::save_node_to_file(self.handle, &self.session, &filename)
547    }
548
549    /// Loads and creates a previously saved node and all its contents from given file.
550    pub fn load_from_file(
551        session: &Session,
552        parent: impl Into<Option<NodeHandle>>,
553        label: &str,
554        cook: bool,
555        file: impl AsRef<OsStr>,
556    ) -> Result<HoudiniNode> {
557        debug_assert!(session.is_valid());
558        debug!("Loading node from file {:?}", file.as_ref());
559        let filename = CString::new(file.as_ref().to_string_lossy().to_string())?;
560        let label = CString::new(label)?;
561        let id = crate::ffi::load_node_from_file(parent.into(), session, &label, &filename, cook)?;
562        NodeHandle(id).to_node(session)
563    }
564
565    /// Returns a node preset as bytes.
566    pub fn get_preset(&self, name: &str, preset_type: PresetType) -> Result<Vec<i8>> {
567        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
568        let name = CString::new(name)?;
569        crate::ffi::get_preset(&self.session, self.handle, &name, preset_type)
570    }
571
572    /// Set the preset data to the node.
573    pub fn set_preset(&self, name: &str, preset_type: PresetType, data: &[i8]) -> Result<()> {
574        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
575        let name = CString::new(name)?;
576        crate::ffi::set_preset(&self.session, self.handle, &name, preset_type, data)
577    }
578
579    /// Return Geometry for this node if it's a SOP node,
580    /// otherwise find a child SOP node with display flag and return.
581    pub fn geometry(&self) -> Result<Option<Geometry>> {
582        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
583        match self.info.node_type() {
584            NodeType::Sop => Ok(Some(Geometry {
585                node: self.clone(),
586                info: GeoInfo::from_node(self)?,
587            })),
588            NodeType::Obj => {
589                let info = crate::ffi::get_geo_display_info(self).map(GeoInfo)?;
590                Ok(Some(Geometry {
591                    node: info.node_id().to_node(&self.session)?,
592                    info,
593                }))
594            }
595            _ => Ok(None),
596        }
597    }
598
599    /// Search this node for TOP networks
600    pub fn find_top_networks(&self) -> Result<Vec<HoudiniNode>> {
601        find_networks_nodes(&self.session, NodeType::Top, self, true)
602    }
603
604    /// How many geometry output nodes there is inside an Object or SOP node.
605    pub fn number_of_geo_outputs(&self) -> Result<i32> {
606        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
607        crate::ffi::get_output_geo_count(self)
608    }
609
610    /// Get names of each HDA output
611    pub fn get_output_names(&self) -> Result<Vec<String>> {
612        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
613        crate::ffi::get_output_names(self)
614    }
615
616    /// Return all output nodes as Geometry.
617    pub fn geometry_output_nodes(&self) -> Result<Vec<Geometry>> {
618        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
619        crate::ffi::get_output_geos(self).map(|vec| {
620            vec.into_iter()
621                .map(|inner| {
622                    NodeHandle(inner.nodeId)
623                        .to_node(&self.session)
624                        .map(|node| Geometry {
625                            node,
626                            info: GeoInfo(inner),
627                        })
628                })
629                .collect::<Result<Vec<_>>>()
630        })?
631    }
632
633    /// If node is an Object, return it's transform.
634    pub fn get_transform(
635        &self,
636        rst_order: Option<RSTOrder>,
637        relative_to: impl Into<Option<NodeHandle>>,
638    ) -> Result<Transform> {
639        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
640        crate::ffi::get_object_transform(
641            &self.session,
642            self.handle,
643            relative_to.into(),
644            rst_order.unwrap_or(RSTOrder::Default),
645        )
646        .map(Transform)
647    }
648
649    /// Set transform on the Object
650    pub fn set_transform(&self, transform: &TransformEuler) -> Result<()> {
651        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
652        crate::ffi::set_object_transform(&self.session, self.handle, &transform.0)
653    }
654
655    /// Set keyframes animation on the Object.
656    pub fn set_transform_anim_curve(
657        &self,
658        component: TransformComponent,
659        keys: &[KeyFrame],
660    ) -> Result<()> {
661        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
662        let keys =
663            unsafe { std::mem::transmute::<&[KeyFrame], &[crate::ffi::raw::HAPI_Keyframe]>(keys) };
664        crate::ffi::set_transform_anim_curve(&self.session, self.handle, component, keys)
665    }
666
667    /// Connect output of another node into an input on this node.
668    pub fn connect_input<H: Into<NodeHandle>>(
669        &self,
670        input_num: i32,
671        source: H,
672        output_num: i32,
673    ) -> Result<()> {
674        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
675        crate::ffi::connect_node_input(
676            &self.session,
677            self.handle,
678            input_num,
679            source.into(),
680            output_num,
681        )
682    }
683
684    /// Get the nodes currently connected to the given node at the output index.
685    pub fn output_connected_nodes(
686        &self,
687        output_index: i32,
688        search_subnets: bool,
689    ) -> Result<Vec<NodeHandle>> {
690        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
691        crate::ffi::query_node_output_connected_nodes(self, output_index, search_subnets)
692    }
693
694    /// Disconnect a given input index.
695    pub fn disconnect_input(&self, input_index: i32) -> Result<()> {
696        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
697        crate::ffi::disconnect_node_input(self, input_index)
698    }
699
700    /// Disconnect a given output index.
701    pub fn disconnect_outputs(&self, output_index: i32) -> Result<()> {
702        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
703        crate::ffi::disconnect_node_outputs(self, output_index)
704    }
705
706    /// Set display flag on this node.
707    pub fn set_display_flag(&self, on: bool) -> Result<()> {
708        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
709        crate::ffi::set_node_display(&self.session, self.handle, on)
710    }
711
712    /// Get the name of a node's input.
713    pub fn get_input_name(&self, input_index: i32) -> Result<String> {
714        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
715        crate::ffi::get_node_input_name(self, input_index)
716    }
717
718    /// Get the ids of the message nodes specified in the HDA Type Properties
719    pub fn get_message_nodes(&self) -> Result<Vec<NodeHandle>> {
720        debug_assert!(self.is_valid()?, "Invalid node: {}", self.path()?);
721        crate::ffi::get_message_node_ids(self)
722    }
723}