5
5
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- use std:: {
9
- fmt,
10
- ops:: {
11
- Bound :: { Excluded , Included , Unbounded } ,
12
- RangeBounds ,
13
- } ,
14
- } ;
8
+ use std:: fmt;
15
9
16
10
use godot_ffi as sys;
17
11
use godot_ffi:: { ffi_methods, GodotFfi } ;
@@ -47,33 +41,80 @@ impl NodePath {
47
41
Self { opaque }
48
42
}
49
43
44
+ /// Returns the node name at position `index`.
45
+ ///
46
+ /// If you want to get a property name instead, check out [`get_subname()`][Self::get_subname].
47
+ ///
48
+ /// # Example
49
+ /// ```no_run
50
+ /// # use godot::prelude::*;
51
+ /// let path = NodePath::from("../RigidBody2D/Sprite2D");
52
+ /// godot_print!("{}", path.get_name(0)); // ".."
53
+ /// godot_print!("{}", path.get_name(1)); // "RigidBody2D"
54
+ /// godot_print!("{}", path.get_name(2)); // "Sprite"
55
+ /// ```
56
+ ///
57
+ /// # Panics
58
+ /// In Debug mode, if `index` is out of bounds. In Release, a Godot error is generated and the result is unspecified (but safe).
59
+ pub fn get_name ( & self , index : usize ) -> StringName {
60
+ let inner = self . as_inner ( ) ;
61
+ let index = index as i64 ;
62
+
63
+ debug_assert ! (
64
+ index < inner. get_name_count( ) ,
65
+ "NodePath '{self}': name at index {index} is out of bounds"
66
+ ) ;
67
+
68
+ inner. get_name ( index)
69
+ }
70
+
71
+ /// Returns the node subname (property) at position `index`.
72
+ ///
73
+ /// If you want to get a node name instead, check out [`get_name()`][Self::get_name].
74
+ ///
75
+ /// # Example
76
+ /// ```no_run
77
+ /// # use godot::prelude::*;
78
+ /// let path = NodePath::from("Sprite2D:texture:resource_name");
79
+ /// godot_print!("{}", path.get_subname(0)); // "texture"
80
+ /// godot_print!("{}", path.get_subname(1)); // "resource_name"
81
+ /// ```
82
+ ///
83
+ /// # Panics
84
+ /// In Debug mode, if `index` is out of bounds. In Release, a Godot error is generated and the result is unspecified (but safe).
85
+ pub fn get_subname ( & self , index : usize ) -> StringName {
86
+ let inner = self . as_inner ( ) ;
87
+ let index = index as i64 ;
88
+
89
+ debug_assert ! (
90
+ index < inner. get_subname_count( ) ,
91
+ "NodePath '{self}': subname at index {index} is out of bounds"
92
+ ) ;
93
+
94
+ inner. get_subname ( index)
95
+ }
96
+
50
97
/// Returns the number of node names in the path. Property subnames are not included.
51
- pub fn get_name_count ( & self ) -> u32 {
98
+ pub fn get_name_count ( & self ) -> usize {
52
99
self . as_inner ( )
53
100
. get_name_count ( )
54
101
. try_into ( )
55
- . expect ( "Godot name counts are non negative int" )
56
- }
57
-
58
- /// Returns the node name indicated by `idx`, starting from 0. If `idx` is out of bounds, [`None`] is returned.
59
- /// See also [`NodePath::get_subname_count`] and [`NodePath::get_name_count`].
60
- pub fn get_name ( & self , idx : u32 ) -> Option < StringName > {
61
- let inner = self . as_inner ( ) ;
62
- let idx = idx as i64 ;
63
- // This check checks both data being empty (get_name_count returns 0) and index out of bounds.
64
- if idx >= inner. get_name_count ( ) {
65
- None
66
- } else {
67
- Some ( inner. get_name ( idx) )
68
- }
102
+ . expect ( "Godot name counts are non-negative ints" )
69
103
}
70
104
71
105
/// Returns the number of property names ("subnames") in the path. Each subname in the node path is listed after a colon character (`:`).
72
- pub fn get_subname_count ( & self ) -> u32 {
106
+ pub fn get_subname_count ( & self ) -> usize {
73
107
self . as_inner ( )
74
108
. get_subname_count ( )
75
109
. try_into ( )
76
- . expect ( "Godot subname counts are non negative int" )
110
+ . expect ( "Godot subname counts are non-negative ints" )
111
+ }
112
+
113
+ /// Returns the total number of names + subnames.
114
+ ///
115
+ /// This method does not exist in Godot and is provided in Rust for convenience.
116
+ pub fn get_total_count ( & self ) -> usize {
117
+ self . get_name_count ( ) + self . get_subname_count ( )
77
118
}
78
119
79
120
/// Returns a 32-bit integer hash value representing the string.
@@ -84,48 +125,38 @@ impl NodePath {
84
125
. expect ( "Godot hashes are uint32_t" )
85
126
}
86
127
87
- /// Returns the property name indicated by `idx`, starting from 0. If `idx` is out of bounds, [`None`] is returned.
88
- /// See also [`NodePath::get_subname_count`].
89
- pub fn get_subname ( & self , idx : u32 ) -> Option < StringName > {
90
- let inner = self . as_inner ( ) ;
91
- let idx = idx as i64 ;
92
- // This check checks both data being empty (get_subname_count returns 0) and index out of bounds.
93
- if idx >= inner. get_subname_count ( ) {
94
- None
95
- } else {
96
- Some ( inner. get_subname ( idx) )
128
+ /// Returns the range `begin..exclusive_end` as a new `NodePath`.
129
+ ///
130
+ /// The absolute value of `begin` and `exclusive_end` will be clamped to [`get_total_count()`][Self::get_total_count].
131
+ /// So, to express "until the end", you can simply pass a large value for `exclusive_end`, such as `i32::MAX`.
132
+ ///
133
+ /// If either `begin` or `exclusive_end` are negative, they will be relative to the end of the `NodePath`. \
134
+ /// For example, `path.subpath(0, -2)` is a shorthand for `path.subpath(0, path.get_total_count() - 2)`.
135
+ ///
136
+ /// _Godot equivalent: `slice`_
137
+ ///
138
+ /// # Compatibility
139
+ /// The `slice()` behavior for Godot <= 4.3 is unintuitive, see [#100954](https://github.com/godotengine/godot/pull/100954). godot-rust
140
+ /// automatically changes this to the fixed version for Godot 4.4+, even when used in older versions. So, the behavior is always the same.
141
+ // i32 used because it can be negative and many Godot APIs use this, see https://github.com/godot-rust/gdext/pull/982/files#r1893732978.
142
+ #[ cfg( since_api = "4.3" ) ]
143
+ #[ doc( alias = "slice" ) ]
144
+ pub fn subpath ( & self , begin : i32 , exclusive_end : i32 ) -> NodePath {
145
+ // Polyfill for bug https://github.com/godotengine/godot/pull/100954.
146
+ // TODO(v0.3) make polyfill (everything but last line) conditional if PR is merged in 4.4.
147
+ let name_count = self . get_name_count ( ) as i32 ;
148
+ let subname_count = self . get_subname_count ( ) as i32 ;
149
+ let total_count = name_count + subname_count;
150
+
151
+ let mut begin = begin. clamp ( -total_count, total_count) ;
152
+ if begin < 0 {
153
+ begin += total_count;
154
+ }
155
+ if begin > name_count {
156
+ begin += 1 ;
97
157
}
98
- }
99
158
100
- /// Returns the slice of the [`NodePath`] as a new [`NodePath`]
101
- pub fn slice ( & self , range : impl RangeBounds < i64 > ) -> NodePath {
102
- self . as_inner ( ) . slice (
103
- match range. start_bound ( ) {
104
- Excluded ( & start) => {
105
- if start == -1 {
106
- // Default end from godot, since the start is excluded.
107
- i32:: MAX as i64
108
- } else {
109
- start + 1
110
- }
111
- }
112
- Included ( & start) => start,
113
- Unbounded => 0 ,
114
- } ,
115
- match range. end_bound ( ) {
116
- Excluded ( & end) => end,
117
- Included ( & end) => {
118
- if end == -1 {
119
- // Default end from godot, since the end is excluded.
120
- i32:: MAX as i64
121
- } else {
122
- end + 1
123
- }
124
- }
125
- // Default end from godot.
126
- Unbounded => i32:: MAX as i64 ,
127
- } ,
128
- )
159
+ self . as_inner ( ) . slice ( begin as i64 , exclusive_end as i64 )
129
160
}
130
161
131
162
crate :: meta:: declare_arg_method! {
0 commit comments