mittens_engine/engine/ecs/component/
component_ref.rs1use crate::engine::ecs::{ComponentId, World};
27
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum ComponentRef {
30 Guid(uuid::Uuid),
31 Query(String),
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum QueryRootMode {
36 SelfSubtree,
37 ParentScope { levels_up: usize },
38 WorldRoot,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub struct ScopedQuery<'a> {
43 pub root_mode: QueryRootMode,
44 pub selector: &'a str,
45}
46
47pub fn parse_scoped_query(input: &str) -> ScopedQuery<'_> {
48 let mut rest = input;
49 let mut levels_up = 0usize;
50 while let Some(next) = rest.strip_prefix("../") {
51 levels_up += 1;
52 rest = next.trim_start();
53 }
54 if levels_up > 0 {
55 return ScopedQuery {
56 root_mode: QueryRootMode::ParentScope { levels_up },
57 selector: rest,
58 };
59 }
60 if let Some(next) = rest.strip_prefix('/') {
61 return ScopedQuery {
62 root_mode: QueryRootMode::WorldRoot,
63 selector: next.trim_start(),
64 };
65 }
66 ScopedQuery {
67 root_mode: QueryRootMode::SelfSubtree,
68 selector: input,
69 }
70}
71
72pub fn resolve_component_ref(
73 world: &World,
74 src: &ComponentRef,
75 owner: Option<ComponentId>,
76 default_root_mode: QueryRootMode,
77) -> Option<ComponentId> {
78 match src {
79 ComponentRef::Guid(uuid) => world.component_id_by_guid(*uuid),
80 ComponentRef::Query(query) => resolve_scoped_query(world, owner, query, default_root_mode),
81 }
82}
83
84pub fn resolve_scoped_query(
85 world: &World,
86 owner: Option<ComponentId>,
87 query: &str,
88 default_root_mode: QueryRootMode,
89) -> Option<ComponentId> {
90 let scoped = parse_scoped_query(query);
91 let root_mode = match scoped.root_mode {
92 QueryRootMode::SelfSubtree if query == scoped.selector => default_root_mode,
93 other => other,
94 };
95 let selector = scoped.selector.trim();
96 if selector.is_empty() {
97 return None;
98 }
99
100 match root_mode {
101 QueryRootMode::SelfSubtree => {
102 let root = owner?;
103 world.find_component(root, selector)
104 }
105 QueryRootMode::ParentScope { levels_up } => {
106 let root = climb_scope(world, owner?, levels_up)?;
107 world.find_component(root, selector)
108 }
109 QueryRootMode::WorldRoot => resolve_world_root_query(world, selector),
110 }
111}
112
113fn climb_scope(world: &World, mut current: ComponentId, levels_up: usize) -> Option<ComponentId> {
114 for _ in 0..levels_up {
115 current = world.parent_of(current)?;
116 }
117 Some(current)
118}
119
120fn resolve_world_root_query(world: &World, selector: &str) -> Option<ComponentId> {
121 if let Some(stripped) = selector.strip_prefix('>') {
122 let child_selector = stripped.trim_start();
123 if child_selector.is_empty() {
124 return None;
125 }
126 return world
127 .world_roots()
128 .into_iter()
129 .find(|&root| world.component_matches_selector(root, child_selector));
130 }
131 world
132 .world_roots()
133 .into_iter()
134 .find_map(|root| world.find_component(root, selector))
135}