1use bevy::{
2 ecs::{
3 archetype::{ArchetypeId, Archetypes},
4 component::{ComponentId, Components, StorageType},
5 entity::{Entities, Entity},
6 },
7 utils::get_short_name,
8};
9use clap::{App, AppSettings, ArgGroup, ArgMatches, arg};
10
11pub fn list_resources(archetypes: &Archetypes, components: &Components) -> String {
12 let mut output = String::new();
13
14 let mut r: Vec<String> = archetypes
15 .resource()
16 .components()
17 .map(|id| components.get_info(id).unwrap())
18 .map(|info| get_short_name(info.name()))
23 .collect();
24
25 r.sort();
27
28 output.push_str("[resource name]\n");
29 r.iter()
30 .for_each(|name| output.push_str(&format!("{}\n", name)));
31
32 output
33}
34
35fn get_components_by_name(
36 components: &Components,
37 short: bool,
38 filter: Option<&str>,
39) -> Vec<(usize, String)> {
40 let mut names = Vec::new();
41 for id in 1..components.len() {
42 if let Some(info) = components.get_info(ComponentId::new(id)) {
43 if short {
44 names.push((id, get_short_name(info.name())));
45 } else {
46 names.push((id, String::from(info.name())));
47 }
48 }
49 }
50
51 if let Some(filter) = filter {
52 names
53 .iter()
54 .cloned()
55 .filter(|(_, name)| name.contains(filter))
56 .collect()
57 } else {
58 names
59 }
60}
61
62fn list_components(c: &Components, short: bool, filter: Option<&str>) -> String {
63 let mut names = get_components_by_name(c, short, filter);
64 names.sort();
65
66 let mut output = String::new();
67 output.push_str("[component id] [component name]\n");
68 names
69 .iter()
70 .for_each(|(id, name)| output.push_str(&format!("{} {}\n", id, name)));
71
72 output
73}
74
75fn list_entities(e: &Entities) -> String {
76 let mut output = String::new();
77 output.push_str(&format!("[entity index] [archetype id]\n"));
78 for id in 0..e.len() {
79 if let Some(entity) = e.resolve_from_id(id) {
80 if let Some(location) = e.get(entity) {
81 output.push_str(&format!("{} {}\n", id, location.archetype_id.index()));
82 }
83 }
84 }
85
86 output
87}
88
89fn list_archetypes(a: &Archetypes) -> String {
90 let mut output = String::new();
91 output.push_str(&format!("[id] [entity count]\n"));
92 a.iter().for_each(|archetype| {
93 output.push_str(&format!(
94 "{} {}\n",
95 archetype.id().index(),
96 archetype.entities().iter().count()
97 ))
98 });
99
100 output
101}
102
103fn print_ecs_counts(a: &Archetypes, c: &Components, e: &Entities) -> String {
104 String::from(format!(
105 "entities: {}, components: {}, archetypes: {}\n",
106 e.len(),
107 c.len(),
108 a.len()
109 ))
110}
111
112fn find_archetypes_by_component_name(
113 a: &Archetypes,
114 c: &Components,
115 component_name: &str,
116) -> String {
117 let components = get_components_by_name(c, false, Some(component_name));
118
119 if components.len() == 0 {
120 return String::from(format!("No component found with name {}\n", component_name));
121 }
122
123 if components.len() > 1 {
124 let mut output = String::new();
125 output.push_str(&format!(
126 "More than one component found with name {}\n",
127 component_name
128 ));
129 output.push_str(&format!(
130 "Consider searching with '--componentid' instead\n\n"
131 ));
132 output.push_str(&format!("[component id] [component name]\n"));
133 components
134 .iter()
135 .for_each(|(id, name)| output.push_str(&format!("{} {}\n", id, name)));
136 return output;
137 }
138
139 if let Some(id_name) = components.iter().next() {
140 return find_archetypes_by_component_id(a, id_name.0);
141 };
142
143 String::from("unsupported command")
145}
146
147fn find_archetypes_by_component_id(a: &Archetypes, component_id: usize) -> String {
148 let mut output = String::new();
149
150 let archetypes = a
151 .iter()
152 .filter(|archetype| archetype.components().any(|c| c.index() == component_id))
153 .map(|archetype| archetype.id().index());
154
155 output.push_str(&format!("archetype ids:\n"));
156 archetypes.for_each(|id| output.push_str(&format!("{}, ", id)));
157 output.push_str("\n");
158
159 output
160}
161
162pub fn get_archetype_id_by_entity_id(a: &Archetypes, entity_id: u32) -> Option<usize> {
163 let mut archetypes = a
164 .iter()
165 .filter(|archetype| archetype.entities().iter().any(|e| e.id() == entity_id))
166 .map(|archetype| archetype.id().index());
167
168 archetypes.next()
169}
170
171fn find_archetype_by_entity_id(a: &Archetypes, entity_id: u32) -> String {
172 let mut output = String::new();
173
174 let archetype_id = get_archetype_id_by_entity_id(a, entity_id);
175
176 output.push_str(&format!("archetype id:\n"));
177 if let Some(id) = archetype_id {
178 output.push_str(&format!("{}", id))
179 }
180
181 output
182}
183
184fn find_entities_by_component_id(a: &Archetypes, component_id: usize) -> String {
185 let entities: Vec<&Entity> = a
186 .iter()
187 .filter(|archetype| archetype.components().any(|c| c.index() == component_id))
188 .map(|archetype| archetype.entities())
189 .flatten()
190 .collect();
191
192 if entities.iter().len() == 0 {
193 let mut output = String::new();
194 output.push_str("no entites found\n");
195 return output;
196 }
197
198 let mut output = String::new();
199 output.push_str(&format!("entity ids:\n"));
200 entities
201 .iter()
202 .for_each(|id| output.push_str(&format!("{}, ", id.id())));
203 output.push_str("\n");
204
205 output
206}
207
208fn find_entities_by_component_name(a: &Archetypes, c: &Components, component_name: &str) -> String {
209 let components = get_components_by_name(c, false, Some(component_name));
210
211 let mut output = String::new();
212 components.iter().for_each(|(id, name)| {
213 output.push_str(&format!("{}\n", name));
214 output.push_str(&find_entities_by_component_id(a, *id));
215 output.push_str("\n");
216 });
217
218 output
219}
220
221fn print_archetype(a: &Archetypes, c: &Components, archetype_id: ArchetypeId) -> String {
222 let mut output = String::new();
223 if let Some(archetype) = a.get(archetype_id) {
224 output.push_str(&format!("id: {:?}\n", archetype.id()));
225 output.push_str(&format!("table_id: {:?}\n", archetype.table_id()));
226 output.push_str(&format!(
227 "entities ({}): ",
228 archetype.entities().iter().count()
229 ));
230 archetype
231 .entities()
232 .iter()
233 .for_each(|entity| output.push_str(&format!("{}, ", entity.id())));
234 output.push_str(&format!("\n"));
235 output.push_str(&format!(
246 "table_components ({}): ",
247 archetype.table_components().iter().count()
248 ));
249 archetype
250 .table_components()
251 .iter()
252 .map(|id| (id.index(), c.get_info(*id).unwrap()))
253 .map(|(id, info)| (id, get_short_name(info.name())))
254 .for_each(|(id, name)| output.push_str(&format!("{} {}, ", id, name)));
255 output.push_str("\n");
256
257 output.push_str(&format!(
258 "sparse set components ({}): ",
259 archetype.sparse_set_components().iter().count()
260 ));
261 archetype
262 .sparse_set_components()
263 .iter()
264 .map(|id| (id.index(), c.get_info(*id).unwrap()))
265 .map(|(id, info)| (id, get_short_name(info.name())))
266 .for_each(|(id, name)| output.push_str(&format!("{} {}, ", id, name)));
267 output.push_str(&format!("\n"));
268 } else {
269 output.push_str(&format!(
270 "No archetype found with id: {}\n",
271 archetype_id.index()
272 ));
273 }
274
275 output
276}
277
278fn print_component(c: &Components, component_id: usize) -> String {
279 let mut output = String::new();
280 if let Some(info) = c.get_info(ComponentId::new(component_id)) {
281 output.push_str(&format!("Name: {}\n", info.name()));
282 output.push_str(&format!("Id: {}\n", info.id().index()));
283 output.push_str("StorageType: ");
284 match info.storage_type() {
285 StorageType::Table => output.push_str("Table\n"),
286 StorageType::SparseSet => output.push_str("SparseSet\n"),
287 }
288 output.push_str(&format!("SendAndSync: {}\n", info.is_send_and_sync()));
289 } else {
290 output.push_str(&format!("No component found with id: {}", component_id));
291 }
292
293 output
294}
295
296fn print_component_by_name(c: &Components, component_name: &str) -> String {
297 let components = get_components_by_name(c, false, Some(component_name));
298
299 let mut output = String::new();
300 components
301 .iter()
302 .for_each(|(id, _)| output.push_str(&format!("{}\n", &print_component(c, *id))));
303
304 output
305}
306
307pub fn build_commands<'a>(app: App<'a>) -> App<'a> {
308 let app = app.subcommand(
309 App::new("counts").about("print counts of archetypes, components, and entities"),
310 )
311 .subcommand(
312 App::new("archetypes")
313 .about("get archetypes info")
314 .alias("archetype")
315 .setting(AppSettings::SubcommandRequiredElseHelp)
316 .subcommand(App::new("list")
317 .about("list all archetypes")
318 )
319 .subcommand(App::new("info")
320 .about("get info of one archetype")
321 .arg(arg!(--id <Id> "id to get"))
322 .group(ArgGroup::new("search params")
323 .args(&["id"])
324 .required(true)
325 )
326 )
327 .subcommand(App::new("find")
328 .about("find a archetype")
329 .args([
330 arg!(--componentid <ComponentId> "find types that have components with ComponentId"),
331 arg!(--componentname <ComponentName> "find types that have components with ComponentName"),
332 arg!(--entityid <EntityId> "find types that have entities with EntityId")
333 ])
334 .group(ArgGroup::new("search params")
335 .args(&["componentid", "componentname", "entityid"])
336 .required(true)
337 )
338 )
339 )
340 .subcommand(
341 App::new("components")
342 .about("get components info")
343 .alias("component")
344 .setting(AppSettings::SubcommandRequiredElseHelp)
345 .subcommand(App::new("list")
346 .about("list all components")
347 .args([
348 arg!(-f --filter [Filter] "filter list"),
349 arg!(-l --long "display long name")
350 ])
351 )
352 .subcommand(App::new("info")
353 .about("get info of one component")
354 .args([
355 arg!(--id <Id> "id to get"),
356 arg!(--name <Name> "name to get")
357 ])
358 .group(ArgGroup::new("search params")
359 .args(&["id", "name"])
360 .required(true)
361 )
362 )
363 )
364 .subcommand(
365 App::new("entities")
366 .about("get entity info")
367 .setting(AppSettings::SubcommandRequiredElseHelp)
368 .subcommand(
369 App::new("list")
370 .about("list all entities")
371 )
372 .subcommand(
373 App::new("find")
374 .about("find entity matching search params")
375 .args([
376 arg!(--componentid <ComponentId> "find types that have components with ComponentId"),
377 arg!(--componentname <ComponentName> "find types that have components with ComponentName")
378 ])
379 .group(ArgGroup::new("search params")
380 .args(&["componentid", "componentname"])
381 .required(true)
382 )
383 )
384 )
385 .subcommand(
386 App::new("resources")
387 .about("get resource info")
388 .setting(AppSettings::SubcommandRequiredElseHelp)
389 .subcommand(
390 App::new("list")
391 .about("list all resources")
392 )
393 );
394
395 app
396}
397
398pub fn match_commands(
399 matches: &ArgMatches,
400 a: &Archetypes,
401 c: &Components,
402 e: &Entities,
403) -> String {
404 match matches.subcommand() {
405 Some(("archetypes", matches)) => match matches.subcommand() {
406 Some(("list", _)) => list_archetypes(a),
407 Some(("find", matches)) => {
408 if let Ok(component_id) = matches.value_of_t("componentid") {
409 find_archetypes_by_component_id(a, component_id)
410 } else if let Some(component_name) = matches.value_of("componentname") {
411 find_archetypes_by_component_name(a, c, component_name)
412 } else if let Ok(entity_id) = matches.value_of_t("entityid") {
413 find_archetype_by_entity_id(a, entity_id)
414 } else {
415 String::from("this line should not be hittable")
417 }
418 }
419 Some(("info", matches)) => {
420 if let Ok(id) = matches.value_of_t("id") {
421 print_archetype(a, c, ArchetypeId::new(id))
422 } else {
423 String::from("this line should not be hittable")
424 }
425 }
426 _ => String::from("this line should not be hittable"),
427 },
428 Some(("components", matches)) => match matches.subcommand() {
429 Some(("list", matches)) => {
430 list_components(c, !matches.is_present("long"), matches.value_of("filter"))
431 }
432 Some(("info", matches)) => {
433 if let Ok(id) = matches.value_of_t("id") {
434 print_component(c, id)
435 } else if let Some(name) = matches.value_of("name") {
436 print_component_by_name(c, name)
437 } else {
438 String::from("this line should not be hittable")
439 }
440 }
441 _ => String::from("this line should not be hittable"),
442 },
443 Some(("entities", matches)) => match matches.subcommand() {
444 Some(("list", _)) => list_entities(e),
445 Some(("find", matches)) => {
446 if let Ok(component_id) = matches.value_of_t("componentid") {
447 find_entities_by_component_id(a, component_id)
448 } else if let Some(component_name) = matches.value_of("componentname") {
449 find_entities_by_component_name(a, c, component_name)
450 } else {
451 String::from("this line should not be hittable")
452 }
453 }
454 _ => String::from("this line should not be hittable"),
455 },
456 Some(("resources", matches)) => match matches.subcommand() {
457 Some(("list", _)) => list_resources(a, c),
458 _ => String::from("this line should not be hittable"),
459 },
460 Some(("counts", _)) => print_ecs_counts(a, c, e),
461 _ => String::from(""),
462 }
463}