From 5dc69be84536b30cd9317b20eb031b0ff89ecb59 Mon Sep 17 00:00:00 2001
From: Kubat <maelle.martin@proton.me>
Date: Thu, 1 May 2025 15:53:28 +0200
Subject: [PATCH] [SPELL::STD] As an example, we implement a log spell

---
 grimoire/src/spell/factory.rs | 13 +++++++-
 grimoire/src/spell/mod.rs     | 11 +++++--
 grimoire/src/spell/std/log.rs | 57 +++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+), 3 deletions(-)
 create mode 100644 grimoire/src/spell/std/log.rs

diff --git a/grimoire/src/spell/factory.rs b/grimoire/src/spell/factory.rs
index 84c1281..53dffa8 100644
--- a/grimoire/src/spell/factory.rs
+++ b/grimoire/src/spell/factory.rs
@@ -8,12 +8,23 @@ use std::collections::HashMap;
 pub type SpellBuilderFunction = fn(SpellArguments) -> Result<Box<dyn Spell>, Error>;
 
 /// The spell factory, you have to register the spell before trying to create them.
-#[derive(Debug, Default)]
+#[derive(Debug)]
 pub struct SpellFactory {
     /// The dispatch map, to get a builder by an associated spell name.
     dispatch: HashMap<&'static str, SpellBuilderFunction>,
 }
 
+impl Default for SpellFactory {
+    fn default() -> Self {
+        use crate::spell::std as spell;
+        let mut this = Self { dispatch: Default::default() };
+
+        this.register::<spell::Log>().expect("error in std spells");
+
+        this
+    }
+}
+
 impl SpellFactory {
     /// Get the list of all registered spells.
     pub fn get_registered_spells(&self) -> impl Iterator<Item = &'static str> {
diff --git a/grimoire/src/spell/mod.rs b/grimoire/src/spell/mod.rs
index 35fd600..aa9e2fa 100644
--- a/grimoire/src/spell/mod.rs
+++ b/grimoire/src/spell/mod.rs
@@ -1,9 +1,16 @@
 //! The definition of Spells in the Grimoire engine.
 
-mod factory;
 mod arguments;
+mod factory;
 mod traits;
 
-pub use factory::*;
+/// Standard spells.
+pub mod std {
+    mod log;
+
+    pub use log::Log;
+}
+
 pub use arguments::*;
+pub use factory::*;
 pub use traits::*;
diff --git a/grimoire/src/spell/std/log.rs b/grimoire/src/spell/std/log.rs
new file mode 100644
index 0000000..5c7ba6f
--- /dev/null
+++ b/grimoire/src/spell/std/log.rs
@@ -0,0 +1,57 @@
+use crate::prelude::*;
+
+#[derive(Debug)]
+pub struct Log {
+    level: String,
+    messages: Vec<String>,
+}
+
+impl Spell for Log {
+    fn cast(&self, state: GrimoireState) -> Result<GrimoireState, GrimoireError> {
+        let (heading, complementary) = match self.messages.split_first() {
+            Some((heading, complementary)) => (heading, complementary),
+            None => return Ok(state),
+        };
+
+        let logger = |ident: bool, str: &str| {
+            let ident = ident.then_some("\t").unwrap_or_default();
+            match self.level.as_str() {
+                "error" => log::error!(target: "grimoire", "{ident}{str}"),
+                _ => log::info!(target: "grimoire", "{ident}{str}"),
+            }
+        };
+
+        logger(false, heading);
+        complementary
+            .iter()
+            .for_each(|complementary| logger(true, complementary));
+
+        Ok(state)
+    }
+}
+
+impl TryFrom<SpellArguments> for Log {
+    type Error = GrimoireError;
+
+    fn try_from(mut args: SpellArguments) -> Result<Self, Self::Error> {
+        let level = args
+            .remove_as("level", GrimoireType::Identifier)?
+            .to_string();
+        args.has_named()
+            .then_some(())
+            .ok_or(GrimoireError::Unexpected("too much named arguments"))?;
+
+        let messages = args
+            .into_iter()
+            .filter_map(|(key, msg)| key.is_none().then(|| msg.to_string()))
+            .collect();
+
+        Ok(Self { level, messages })
+    }
+}
+
+impl BuildableSpell for Log {
+    fn name() -> &'static str {
+        "log"
+    }
+}
-- 
GitLab