about summary refs log tree commit diff
path: root/src/messages.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/messages.rs')
-rw-r--r--src/messages.rs127
1 files changed, 44 insertions, 83 deletions
diff --git a/src/messages.rs b/src/messages.rs
index 948787f1393b..aa0366e786ca 100644
--- a/src/messages.rs
+++ b/src/messages.rs
@@ -1,32 +1,33 @@
+use crate::util::template::Template;
+use crate::util::template::TemplateParams;
 use rand::seq::SliceRandom;
 use rand::Rng;
-use serde::de::MapAccess;
-use serde::de::SeqAccess;
-use serde::de::Visitor;
 use std::collections::HashMap;
-use std::fmt;
-use std::marker::PhantomData;
 
 #[derive(Deserialize, Debug, PartialEq, Eq)]
 #[serde(untagged)]
 enum Message<'a> {
-    Single(&'a str),
-    Choice(Vec<&'a str>),
+    #[serde(borrow)]
+    Single(Template<'a>),
+    Choice(Vec<Template<'a>>),
 }
 
 impl<'a> Message<'a> {
-    fn resolve<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&'a str> {
+    fn resolve<R: Rng + ?Sized>(&self, rng: &mut R) -> Option<&Template<'a>> {
         use Message::*;
         match self {
-            Single(msg) => Some(*msg),
-            Choice(msgs) => msgs.choose(rng).map(|msg| *msg),
+            Single(msg) => Some(msg),
+            Choice(msgs) => msgs.choose(rng),
         }
     }
 }
 
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Deserialize, Debug, PartialEq, Eq)]
+#[serde(untagged)]
 enum NestedMap<'a> {
+    #[serde(borrow)]
     Direct(Message<'a>),
+    #[serde(borrow)]
     Nested(HashMap<&'a str, NestedMap<'a>>),
 }
 
@@ -46,63 +47,6 @@ impl<'a> NestedMap<'a> {
     }
 }
 
-struct NestedMapVisitor<'a> {
-    marker: PhantomData<fn() -> NestedMap<'a>>,
-}
-
-impl<'a> NestedMapVisitor<'a> {
-    fn new() -> Self {
-        NestedMapVisitor {
-            marker: PhantomData,
-        }
-    }
-}
-
-impl<'de> Visitor<'de> for NestedMapVisitor<'de> {
-    type Value = NestedMap<'de>;
-
-    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
-        formatter.write_str(
-            "A message, a list of messages, or a nested map of messages",
-        )
-    }
-
-    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> {
-        Ok(NestedMap::Direct(Message::Single(v)))
-    }
-
-    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
-    where
-        A: SeqAccess<'de>,
-    {
-        let mut choices = Vec::with_capacity(seq.size_hint().unwrap_or(0));
-        while let Some(choice) = seq.next_element()? {
-            choices.push(choice);
-        }
-        Ok(NestedMap::Direct(Message::Choice(choices)))
-    }
-
-    fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
-    where
-        A: MapAccess<'de>,
-    {
-        let mut nested = HashMap::with_capacity(map.size_hint().unwrap_or(0));
-        while let Some((k, v)) = map.next_entry()? {
-            nested.insert(k, v);
-        }
-        Ok(NestedMap::Nested(nested))
-    }
-}
-
-impl<'de> serde::Deserialize<'de> for NestedMap<'de> {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        deserializer.deserialize_any(NestedMapVisitor::new())
-    }
-}
-
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -122,13 +66,18 @@ choice = ["Say this", "Or this"]
             result,
             Ok(NestedMap::Nested(hashmap! {
                 "global" => NestedMap::Nested(hashmap!{
-                    "hello" => NestedMap::Direct(Message::Single("Hello World!")),
+                    "hello" => NestedMap::Direct(Message::Single(Template::parse("Hello World!").unwrap())),
                 }),
                 "foo" => NestedMap::Nested(hashmap!{
                     "bar" => NestedMap::Nested(hashmap!{
-                        "single" => NestedMap::Direct(Message::Single("Single")),
+                        "single" => NestedMap::Direct(Message::Single(
+                            Template::parse("Single").unwrap()
+                        )),
                         "choice" => NestedMap::Direct(Message::Choice(
-                            vec!["Say this", "Or this"]
+                            vec![
+                                Template::parse("Say this").unwrap(),
+                                Template::parse("Or this").unwrap()
+                            ]
                         ))
                     })
                 })
@@ -152,31 +101,43 @@ choice = ["Say this", "Or this"]
 
         assert_eq!(
             map.lookup("global.hello"),
-            Some(&Message::Single("Hello World!"))
+            Some(&Message::Single(Template::parse("Hello World!").unwrap()))
         );
         assert_eq!(
             map.lookup("foo.bar.single"),
-            Some(&Message::Single("Single"))
+            Some(&Message::Single(Template::parse("Single").unwrap()))
         );
         assert_eq!(
             map.lookup("foo.bar.choice"),
-            Some(&Message::Choice(vec!["Say this", "Or this"]))
+            Some(&Message::Choice(vec![
+                Template::parse("Say this").unwrap(),
+                Template::parse("Or this").unwrap()
+            ]))
         );
     }
 }
 
+// static MESSAGES_RAW: &'static str = include_str!("messages.toml");
+
 static_cfg! {
     static ref MESSAGES: NestedMap<'static> = toml_file("messages.toml");
 }
 
-/// Look up a game message based on the given (dot-separated) name, with the
-/// given random generator used to select from choice-based messages
-pub fn message<R: Rng + ?Sized>(name: &str, rng: &mut R) -> &'static str {
-    MESSAGES
-        .lookup(name)
-        .and_then(|msg| msg.resolve(rng))
-        .unwrap_or_else(|| {
+/// Look up and format a game message based on the given (dot-separated) name,
+/// with the given random generator used to select from choice-based messages
+pub fn message<'a, R: Rng + ?Sized>(
+    name: &'static str,
+    rng: &mut R,
+    params: &TemplateParams<'a>,
+) -> String {
+    match MESSAGES.lookup(name).and_then(|msg| msg.resolve(rng)) {
+        Some(msg) => msg.format(params).unwrap_or_else(|e| {
+            error!("Error formatting template: {}", e);
+            "Template Error".to_string()
+        }),
+        None => {
             error!("Message not found: {}", name);
-            "Message not found"
-        })
+            "Template Not Found".to_string()
+        }
+    }
 }