mod helpers;
mod state;
#[cfg(test)]
mod tests;
pub use state::{SimpleQueryCache, SimpleQueryState};
use crate::simple_world::EntityId as SimpleEntityId;
use crate::simple_world::SimpleWorld;
use soroban_sdk::{Env, Symbol, Vec};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QueryStorage {
Table,
Any,
}
#[derive(Debug, Clone)]
pub struct SimpleQuery {
required_components: Vec<Symbol>,
excluded_components: Vec<Symbol>,
any_components: Vec<Symbol>,
storage: QueryStorage,
}
impl SimpleQuery {
pub fn new(env: &Env) -> Self {
Self {
required_components: Vec::new(env),
excluded_components: Vec::new(env),
any_components: Vec::new(env),
storage: QueryStorage::Table,
}
}
pub fn with_component(mut self, component_type: Symbol) -> Self {
self.required_components.push_back(component_type);
self
}
pub fn with_components(mut self, component_types: &[Symbol]) -> Self {
for component_type in component_types {
self.required_components.push_back(component_type.clone());
}
self
}
pub fn without_component(mut self, component_type: Symbol) -> Self {
self.excluded_components.push_back(component_type);
self
}
pub fn without_components(mut self, component_types: &[Symbol]) -> Self {
for component_type in component_types {
self.excluded_components.push_back(component_type.clone());
}
self
}
pub fn with_any_component(mut self, component_type: Symbol) -> Self {
self.any_components.push_back(component_type);
self
}
pub fn with_any_components(mut self, component_types: &[Symbol]) -> Self {
for component_type in component_types {
self.any_components.push_back(component_type.clone());
}
self
}
pub fn include_sparse(mut self) -> Self {
self.storage = QueryStorage::Any;
self
}
pub fn is_empty(&self) -> bool {
self.required_components.is_empty()
&& self.excluded_components.is_empty()
&& self.any_components.is_empty()
}
pub fn storage(&self) -> QueryStorage {
self.storage
}
pub fn execute(&self, world: &SimpleWorld, env: &Env) -> Vec<SimpleEntityId> {
let candidates = self.candidate_entities(world, env);
let mut results = Vec::new(env);
for i in 0..candidates.len() {
if let Some(entity_id) = candidates.get(i) {
if self.matches(world, entity_id) {
results.push_back(entity_id);
}
}
}
results
}
pub fn candidate_count(&self, world: &SimpleWorld, env: &Env) -> usize {
self.candidate_entities(world, env)
.len()
.try_into()
.unwrap()
}
fn candidate_entities(&self, world: &SimpleWorld, env: &Env) -> Vec<SimpleEntityId> {
let mut best: Option<Vec<SimpleEntityId>> = None;
for i in 0..self.required_components.len() {
if let Some(component_type) = self.required_components.get(i) {
let entities = self.entities_for_component(world, &component_type, env);
if best
.as_ref()
.map(|current| entities.len() < current.len())
.unwrap_or(true)
{
best = Some(entities);
}
}
}
if let Some(candidates) = best {
return candidates;
}
if !self.any_components.is_empty() {
let mut union = Vec::new(env);
for i in 0..self.any_components.len() {
if let Some(component_type) = self.any_components.get(i) {
let entities = self.entities_for_component(world, &component_type, env);
for j in 0..entities.len() {
if let Some(entity_id) = entities.get(j) {
if !helpers::contains_entity(&union, entity_id) {
union.push_back(entity_id);
}
}
}
}
}
return union;
}
let mut all_entities = Vec::new(env);
for entity_id in world.entity_components.keys().iter() {
all_entities.push_back(entity_id);
}
all_entities
}
fn entities_for_component(
&self,
world: &SimpleWorld,
component_type: &Symbol,
env: &Env,
) -> Vec<SimpleEntityId> {
match self.storage {
QueryStorage::Table => world.get_table_entities_with_component(component_type, env),
QueryStorage::Any => world.get_all_entities_with_component(component_type, env),
}
}
fn matches(&self, world: &SimpleWorld, entity_id: SimpleEntityId) -> bool {
for i in 0..self.required_components.len() {
if let Some(component_type) = self.required_components.get(i) {
if !world.has_component(entity_id, &component_type) {
return false;
}
}
}
for i in 0..self.excluded_components.len() {
if let Some(component_type) = self.excluded_components.get(i) {
if world.has_component(entity_id, &component_type) {
return false;
}
}
}
if self.any_components.is_empty() {
return true;
}
for i in 0..self.any_components.len() {
if let Some(component_type) = self.any_components.get(i) {
if world.has_component(entity_id, &component_type) {
return true;
}
}
}
false
}
}
pub struct SimpleQueryBuilder {
query: SimpleQuery,
}
impl SimpleQueryBuilder {
pub fn new(env: &Env) -> Self {
Self {
query: SimpleQuery::new(env),
}
}
pub fn with_component(mut self, component_type: Symbol) -> Self {
self.query = self.query.with_component(component_type);
self
}
pub fn with_components(mut self, component_types: &[Symbol]) -> Self {
self.query = self.query.with_components(component_types);
self
}
pub fn without_component(mut self, component_type: Symbol) -> Self {
self.query = self.query.without_component(component_type);
self
}
pub fn without_components(mut self, component_types: &[Symbol]) -> Self {
self.query = self.query.without_components(component_types);
self
}
pub fn with_any_component(mut self, component_type: Symbol) -> Self {
self.query = self.query.with_any_component(component_type);
self
}
pub fn with_any_components(mut self, component_types: &[Symbol]) -> Self {
self.query = self.query.with_any_components(component_types);
self
}
pub fn include_sparse(mut self) -> Self {
self.query = self.query.include_sparse();
self
}
pub fn build(self) -> SimpleQuery {
self.query
}
pub fn build_state(self, env: &Env) -> SimpleQueryState {
SimpleQueryState::new(self.query, env)
}
}