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}