From 4af1f65d9cbdb6a24c4a1b900e89a4906564d1b8 Mon Sep 17 00:00:00 2001 From: Kubat <maelle.martin@proton.me> Date: Sat, 19 Apr 2025 13:25:50 +0200 Subject: [PATCH] Organise the Spell things --- grimoire_engine_types/src/error.rs | 8 ++- grimoire_engine_types/src/spell.rs | 54 -------------------- grimoire_engine_types/src/spell/arguments.rs | 1 + grimoire_engine_types/src/spell/factory.rs | 44 ++++++++++++++++ grimoire_engine_types/src/spell/mod.rs | 7 +++ grimoire_engine_types/src/spell/traits.rs | 30 +++++++++++ grimoire_engine_types/src/state.rs | 3 +- 7 files changed, 90 insertions(+), 57 deletions(-) delete mode 100644 grimoire_engine_types/src/spell.rs create mode 100644 grimoire_engine_types/src/spell/arguments.rs create mode 100644 grimoire_engine_types/src/spell/factory.rs create mode 100644 grimoire_engine_types/src/spell/mod.rs create mode 100644 grimoire_engine_types/src/spell/traits.rs diff --git a/grimoire_engine_types/src/error.rs b/grimoire_engine_types/src/error.rs index 482b170..dbe8c26 100644 --- a/grimoire_engine_types/src/error.rs +++ b/grimoire_engine_types/src/error.rs @@ -1,2 +1,8 @@ #[derive(Debug, thiserror::Error)] -pub enum SpellError {} +pub enum SpellError { + #[error("Already defined spell '{0}': {1}")] + Redefined(&'static str, &'static str), + + #[error("Undefined spell '{0}'")] + Undefined(String), +} diff --git a/grimoire_engine_types/src/spell.rs b/grimoire_engine_types/src/spell.rs deleted file mode 100644 index 8db3e85..0000000 --- a/grimoire_engine_types/src/spell.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::{error::SpellError, state::State}; - -pub trait Spell { - fn cast(&self, state: State) -> Result<State, SpellError>; - - fn uncast(&self, state: State) -> Result<State, SpellError>; -} - -pub struct SpellArguments {} - -pub trait BuildableSpell: Spell + TryFrom<SpellArguments, Error = SpellError> { - fn name() -> &'static str; - - fn aliases() -> &'static [&'static str] { - &[] - } - - fn name_list() -> impl Iterator<Item = &'static str> { - [Self::name()] - .into_iter() - .chain(Self::aliases().iter().copied()) - } - - fn create(arguments: SpellArguments) -> Result<Box<dyn Spell>, SpellError>; -} - -type SpellBuilderFunction = fn(SpellArguments) -> Result<Box<dyn Spell>, SpellError>; - -#[derive(Debug, Default)] -pub struct SpellFactory { - dispatch: hashbrown::HashMap<&'static str, SpellBuilderFunction>, -} - -impl SpellFactory { - pub fn register<Spell: BuildableSpell + 'static>(&mut self) -> Result<(), SpellError> { - Spell::name_list() - .find(|spell_name| self.dispatch.contains_key(spell_name)) - .map(|_name| -> Result<(), SpellError> { todo!("error") }) - .unwrap_or(Ok(()))?; - - Spell::name_list().for_each(|name| _ = self.dispatch.insert(name, Spell::create)); - - Ok(()) - } - - pub fn try_build_spell( - &self, - name: &str, - arguments: SpellArguments, - ) -> Result<Box<dyn Spell>, SpellError> { - let builder_function = self.dispatch.get(name).ok_or_else(|| todo!("error"))?; - builder_function(arguments) - } -} diff --git a/grimoire_engine_types/src/spell/arguments.rs b/grimoire_engine_types/src/spell/arguments.rs new file mode 100644 index 0000000..6fb7e74 --- /dev/null +++ b/grimoire_engine_types/src/spell/arguments.rs @@ -0,0 +1 @@ +pub struct SpellArguments {} diff --git a/grimoire_engine_types/src/spell/factory.rs b/grimoire_engine_types/src/spell/factory.rs new file mode 100644 index 0000000..8c9ec57 --- /dev/null +++ b/grimoire_engine_types/src/spell/factory.rs @@ -0,0 +1,44 @@ +use crate::{ + error::SpellError, + spell::{BuildableSpell, Spell, SpellArguments}, +}; +use std::collections::HashMap; + +/// The builder function, call it with some arguments to try to build the spell +pub type SpellBuilderFunction = fn(SpellArguments) -> Result<Box<dyn Spell>, SpellError>; + +/// The spell factory, you have to register the spell before trying to create them. +#[derive(Debug, Default)] +pub struct SpellFactory { + /// The dispatch map, to get a builder by an associated spell name. + dispatch: HashMap<&'static str, SpellBuilderFunction>, +} + +impl SpellFactory { + /// Get the list of all registered spells. + pub fn get_registered_spells(&self) -> impl Iterator<Item = &'static str> { + self.dispatch.keys().copied() + } + + /// Register a spell into the factory. In case of re-registering returns an [Err]. + pub fn register<Sp: BuildableSpell + 'static>(&mut self) -> Result<(), SpellError> { + Sp::name_list().try_for_each(|name| match self.dispatch.entry(name) { + std::collections::hash_map::Entry::Occupied(_) => Err(SpellError::Redefined( + Sp::name(), + "name or alias was already registered", + )), + std::collections::hash_map::Entry::Vacant(entry) => { + _ = entry.insert(Sp::create); + Ok(()) + } + }) + } + + /// Get the builder of a [Spell] by its name. In case of undefined [Spell], returns an [Err]. + pub fn get_builder(&self, name: impl AsRef<str>) -> Result<SpellBuilderFunction, SpellError> { + self.dispatch + .get(name.as_ref()) + .copied() + .ok_or(SpellError::Undefined(name.as_ref().to_string())) + } +} diff --git a/grimoire_engine_types/src/spell/mod.rs b/grimoire_engine_types/src/spell/mod.rs new file mode 100644 index 0000000..646ce42 --- /dev/null +++ b/grimoire_engine_types/src/spell/mod.rs @@ -0,0 +1,7 @@ +mod factory; +mod arguments; +mod traits; + +pub use factory::*; +pub use arguments::*; +pub use traits::*; diff --git a/grimoire_engine_types/src/spell/traits.rs b/grimoire_engine_types/src/spell/traits.rs new file mode 100644 index 0000000..8c6c830 --- /dev/null +++ b/grimoire_engine_types/src/spell/traits.rs @@ -0,0 +1,30 @@ +use crate::{error::SpellError, spell::SpellArguments, state::State}; + +pub trait Spell { + fn cast(&self, state: State) -> Result<State, SpellError>; + + fn uncast(&self, state: State) -> Result<State, SpellError>; +} + +pub trait BuildableSpell +where + Self: Spell + TryFrom<SpellArguments, Error = SpellError> + 'static, +{ + fn name() -> &'static str; + + fn aliases() -> &'static [&'static str] { + &[] + } + + fn name_list() -> impl Iterator<Item = &'static str> { + [Self::name()] + .into_iter() + .chain(Self::aliases().iter().copied()) + } + + fn create(arguments: SpellArguments) -> Result<Box<dyn Spell>, SpellError> { + Self::try_from(arguments) + .map(Box::new) + .map(|boxed| boxed as Box<dyn Spell>) + } +} diff --git a/grimoire_engine_types/src/state.rs b/grimoire_engine_types/src/state.rs index 815e4be..02d38c7 100644 --- a/grimoire_engine_types/src/state.rs +++ b/grimoire_engine_types/src/state.rs @@ -1,2 +1 @@ -pub struct State { -} +pub struct State {} -- GitLab