godot_bevy/
node_tree_view.rs1use thiserror::Error;
2
3#[non_exhaustive]
4#[derive(Debug, Error)]
5pub enum NodeTreeViewError {
6 #[error("Could not find node matching pattern: {0}")]
7 NodeNotFound(String),
8}
9
10pub trait NodeTreeView {
14 fn from_node<T: godot::obj::Inherits<godot::classes::Node>>(
16 node: godot::obj::Gd<T>,
17 ) -> Result<Self, NodeTreeViewError>
18 where
19 Self: Sized;
20}
21
22pub fn find_node_by_pattern(
29 base_node: &godot::obj::Gd<godot::classes::Node>,
30 pattern: &str,
31) -> Option<godot::obj::Gd<godot::classes::Node>> {
32 let (search_root, pattern_parts) = if let Some(stripped) = pattern.strip_prefix('/') {
34 let scene_tree = base_node.get_tree()?;
36 let root = scene_tree.get_root()?;
37 let root_as_node = root.upcast::<godot::classes::Node>();
38 let mut parts: Vec<&str> = stripped.split('/').filter(|s| !s.is_empty()).collect();
39
40 if !parts.is_empty() && parts[0] == "root" {
42 parts.remove(0);
43 }
44
45 (root_as_node, parts)
46 } else {
47 let parts: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
49 (base_node.clone(), parts)
50 };
51
52 find_node_recursive(&search_root, &pattern_parts, 0)
53}
54
55fn find_node_recursive(
56 current_node: &godot::obj::Gd<godot::classes::Node>,
57 pattern_parts: &[&str],
58 depth: usize,
59) -> Option<godot::obj::Gd<godot::classes::Node>> {
60 if depth >= pattern_parts.len() {
62 return Some(current_node.clone());
63 }
64
65 let pattern_part = pattern_parts[depth];
66
67 if pattern_part == "*" {
69 for i in 0..current_node.get_child_count() {
71 if let Some(child) = current_node.get_child(i)
72 && let Some(result) = find_node_recursive(&child, pattern_parts, depth + 1)
73 {
74 return Some(result);
75 }
76 }
77 } else if pattern_part.contains('*') {
78 for i in 0..current_node.get_child_count() {
80 if let Some(child) = current_node.get_child(i) {
81 let child_name = child.get_name().to_string();
82 if matches_wildcard_pattern(&child_name, pattern_part)
83 && let Some(result) = find_node_recursive(&child, pattern_parts, depth + 1)
84 {
85 return Some(result);
86 }
87 }
88 }
89 } else {
90 if current_node.has_node(pattern_part) {
92 let child = current_node.get_node_as::<godot::classes::Node>(pattern_part);
93 if let Some(result) = find_node_recursive(&child, pattern_parts, depth + 1) {
94 return Some(result);
95 }
96 }
97 }
98
99 None
100}
101
102fn matches_wildcard_pattern(text: &str, pattern: &str) -> bool {
103 if pattern == "*" {
104 return true;
105 }
106
107 if pattern.starts_with('*') && pattern.len() > 1 {
109 let suffix = &pattern[1..];
110 return text.ends_with(suffix);
111 }
112
113 if pattern.ends_with('*') && pattern.len() > 1 {
114 let prefix = &pattern[..pattern.len() - 1];
115 return text.starts_with(prefix);
116 }
117
118 if let Some(star_pos) = pattern.find('*') {
120 let prefix = &pattern[..star_pos];
121 let suffix = &pattern[star_pos + 1..];
122 return text.starts_with(prefix)
123 && text.ends_with(suffix)
124 && text.len() >= prefix.len() + suffix.len();
125 }
126
127 text == pattern
129}