use crate::commands::CommandQueue;
use crate::scheduler::{ScheduleStage, SystemConfig};
use crate::simple_world::SimpleWorld;
pub struct SystemContext<'w, 'e, 'c> {
world: &'w mut SimpleWorld,
env: &'e soroban_sdk::Env,
commands: &'c mut CommandQueue,
}
impl<'w, 'e, 'c> SystemContext<'w, 'e, 'c> {
pub fn new(
world: &'w mut SimpleWorld,
env: &'e soroban_sdk::Env,
commands: &'c mut CommandQueue,
) -> Self {
Self {
world,
env,
commands,
}
}
pub fn world(&self) -> &SimpleWorld {
self.world
}
pub fn world_mut(&mut self) -> &mut SimpleWorld {
self.world
}
pub fn env(&self) -> &soroban_sdk::Env {
self.env
}
pub fn commands(&mut self) -> &mut CommandQueue {
self.commands
}
}
pub trait SimpleSystem {
fn run(&mut self, context: &mut SystemContext<'_, '_, '_>);
}
pub trait AppSystem: SimpleSystem {}
impl<T: SimpleSystem + ?Sized> AppSystem for T {}
pub struct SystemSpec<S> {
name: &'static str,
system: S,
config: SystemConfig,
}
impl<S> SystemSpec<S> {
pub fn new(name: &'static str, system: S) -> Self {
Self {
name,
system,
config: SystemConfig::default(),
}
}
pub fn in_stage(mut self, stage: ScheduleStage) -> Self {
self.config = self.config.in_stage(stage);
self
}
pub fn after(mut self, system_name: impl Into<alloc::string::String>) -> Self {
self.config = self.config.after(system_name);
self
}
pub fn before(mut self, system_name: impl Into<alloc::string::String>) -> Self {
self.config = self.config.before(system_name);
self
}
pub fn in_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
self.config = self.config.in_set(set_name);
self
}
pub fn after_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
self.config = self.config.after_set(set_name);
self
}
pub fn before_set(mut self, set_name: impl Into<alloc::string::String>) -> Self {
self.config = self.config.before_set(set_name);
self
}
pub fn with_config(mut self, config: SystemConfig) -> Self {
self.config = config;
self
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn config(&self) -> &SystemConfig {
&self.config
}
pub(crate) fn into_parts(self) -> (&'static str, SystemConfig, S) {
(self.name, self.config, self.system)
}
}
pub struct ContextSystem<F> {
function: F,
}
impl<F> ContextSystem<F> {
pub fn new(function: F) -> Self {
Self { function }
}
}
impl<F> SimpleSystem for ContextSystem<F>
where
F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
{
fn run(&mut self, context: &mut SystemContext<'_, '_, '_>) {
(self.function)(context);
}
}
pub struct WorldSystem<F> {
function: F,
}
impl<F> WorldSystem<F> {
pub fn new(function: F) -> Self {
Self { function }
}
}
impl<F> SimpleSystem for WorldSystem<F>
where
F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
{
fn run(&mut self, context: &mut SystemContext<'_, '_, '_>) {
let env = context.env().clone();
(self.function)(context.world_mut(), &env);
}
}
pub fn context_system<F>(function: F) -> ContextSystem<F>
where
F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
{
ContextSystem::new(function)
}
pub fn world_system<F>(function: F) -> WorldSystem<F>
where
F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
{
WorldSystem::new(function)
}
pub fn named_system<F>(name: &'static str, function: F) -> SystemSpec<WorldSystem<F>>
where
F: FnMut(&mut SimpleWorld, &soroban_sdk::Env),
{
SystemSpec::new(name, world_system(function))
}
pub fn named_context_system<F>(name: &'static str, function: F) -> SystemSpec<ContextSystem<F>>
where
F: for<'w, 'e, 'c> FnMut(&mut SystemContext<'w, 'e, 'c>),
{
SystemSpec::new(name, context_system(function))
}
pub fn named_app_system<S>(name: &'static str, system: S) -> SystemSpec<S>
where
S: AppSystem,
{
SystemSpec::new(name, system)
}
#[cfg(test)]
mod tests {
use super::{
named_context_system, named_system, AppSystem, ContextSystem, SimpleSystem, SystemContext,
WorldSystem,
};
use crate::commands::CommandQueue;
use crate::simple_world::SimpleWorld;
use soroban_sdk::{symbol_short, Bytes, Env};
#[test]
fn world_system_wraps_world_and_env_closure() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let mut commands = CommandQueue::new();
let mut system = WorldSystem::new(|world: &mut SimpleWorld, env: &Env| {
let entity = world.spawn_entity();
world.add_component(entity, symbol_short!("tag"), Bytes::from_array(env, &[1]));
});
let mut context = SystemContext::new(&mut world, &env, &mut commands);
system.run(&mut context);
assert!(world.has_component(1, &symbol_short!("tag")));
}
#[test]
fn context_system_wraps_context_closure() {
let env = Env::default();
let mut world = SimpleWorld::new(&env);
let mut commands = CommandQueue::new();
let mut system = ContextSystem::new(|context: &mut SystemContext<'_, '_, '_>| {
context.commands().spawn();
});
let mut context = SystemContext::new(&mut world, &env, &mut commands);
system.run(&mut context);
let spawned = commands.apply(&mut world);
assert_eq!(spawned.len(), 1);
}
#[test]
fn named_helpers_preserve_registration_name() {
let world_spec = named_system("tick", |_world: &mut SimpleWorld, _env: &Env| {});
let context_spec = named_context_system("ctx", |_context| {});
assert_eq!(world_spec.name(), "tick");
assert_eq!(context_spec.name(), "ctx");
}
fn accepts_app_system<S: AppSystem>(_system: &S) {}
#[test]
fn runtime_adapters_implement_app_system() {
let world_system = WorldSystem::new(|_world: &mut SimpleWorld, _env: &Env| {});
let context_system = ContextSystem::new(|_context: &mut SystemContext<'_, '_, '_>| {});
accepts_app_system(&world_system);
accepts_app_system(&context_system);
}
}