Skip to main content

pite_core/
node.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use std::fmt;
3
4use crate::Props;
5
6/// Unique identifier for a node within a [`NodeTree`](crate::NodeTree).
7///
8/// Node identifiers are human-readable strings (e.g. `root`, `player`, `hud`).
9/// They remain stable across scene edits, read cleanly in version-control diffs,
10/// and map directly to scripting runtimes without requiring integer translation tables.
11#[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct NodeId(String);
13
14impl NodeId {
15    /// Creates a new `NodeId` from a string or string slice.
16    pub fn new(id: impl Into<String>) -> Self {
17        Self(id.into())
18    }
19
20    /// Returns a string slice of the node identifier.
21    pub fn as_str(&self) -> &str {
22        &self.0
23    }
24
25    /// Returns `true` if the node identifier is empty.
26    pub fn is_empty(&self) -> bool {
27        self.0.is_empty()
28    }
29}
30
31impl fmt::Display for NodeId {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        f.write_str(&self.0)
34    }
35}
36
37impl fmt::Debug for NodeId {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "NodeId({:?})", self.0)
40    }
41}
42
43impl From<&str> for NodeId {
44    fn from(value: &str) -> Self {
45        Self::new(value)
46    }
47}
48
49impl From<String> for NodeId {
50    fn from(value: String) -> Self {
51        Self::new(value)
52    }
53}
54
55impl AsRef<str> for NodeId {
56    fn as_ref(&self) -> &str {
57        &self.0
58    }
59}
60
61/// Script attachment referencing a project-relative Python file and target class.
62///
63/// Each script file exports a single class, providing a direct 1-to-1 mapping
64/// between the scene node and its scripting logic.
65#[derive(Clone, Debug, Default, PartialEq, Eq)]
66pub struct ScriptRef {
67    /// Project-relative path to the script file (e.g. `res://scripts/player.py`).
68    pub path: String,
69    /// Name of the Python class inheriting from an engine node type.
70    pub class_name: String,
71}
72
73/// A node instance within a [`NodeTree`](crate::NodeTree).
74///
75/// Contains pure state data (identifiers, properties, and tree links). Dynamic behaviors
76/// are handled externally by [`crate::NodeTypeRegistry`] and [`crate::LifecycleSink`].
77///
78/// The enclosing [`NodeTree`](crate::NodeTree) maintains structural invariants (such as
79/// parent-child consistency).
80#[derive(Clone, Debug, Default, PartialEq)]
81pub struct Node {
82    /// Unique identifier of this node.
83    pub id: NodeId,
84    /// Registered node type name (e.g. `Node2D`, `Sprite2D`).
85    pub type_name: String,
86    /// Display name shown in editor and diagnostics.
87    pub name: String,
88    /// Optional identifier of the parent node.
89    pub parent: Option<NodeId>,
90    /// Ordered list of child node identifiers.
91    pub children: Vec<NodeId>,
92    /// Property map containing scalar, vector, and color attributes.
93    pub props: Props,
94    /// Optional attached Python script reference.
95    pub script: Option<ScriptRef>,
96}
97
98impl Node {
99    /// Constructs a minimal node record. Parent and child links are populated by the tree.
100    pub fn new(id: NodeId, type_name: impl Into<String>) -> Self {
101        Self {
102            id,
103            type_name: type_name.into(),
104            name: String::new(),
105            ..Self::default()
106        }
107    }
108}
109
110/// Descriptor used to construct a [`Node`], typically parsed from a `.ptscn` scene file.
111#[derive(Clone, Debug, Default, PartialEq)]
112pub struct NodeDesc {
113    /// Unique identifier for the node.
114    pub id: NodeId,
115    /// Concrete node type name.
116    pub type_name: String,
117    /// Human-friendly display name.
118    pub name: String,
119    /// Identifier of the parent node, if any.
120    pub parent: Option<NodeId>,
121    /// Node properties table.
122    pub props: Props,
123    /// Attached script reference, if any.
124    pub script: Option<ScriptRef>,
125}
126
127impl NodeDesc {
128    /// Constructs a new node descriptor with default properties.
129    pub fn new(id: impl Into<NodeId>, type_name: impl Into<String>) -> Self {
130        Self {
131            id: id.into(),
132            type_name: type_name.into(),
133            ..Self::default()
134        }
135    }
136}