about summary refs log blame commit diff
path: root/src/messages.rs
blob: 719389fa613608a74180647bb5c07bfa56a79749 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                          

                           
                              


                                            
                      


                              


                      
                                                                              

                       

                                             



         

                                            
                    
                    
                        
                    


















                                                                
            
                      
















                                                       
                                                                                                            


                                                        


                                                                      
                                                                      



                                                                     






















                                                     
                                                                            


                                         
                                                                      


                                         



                                                     



          

                                                                     

                                                                         

 






                                                          






                                                                              
                          




                                                            
                                                  


                                            
 















                                          
use crate::util::template::Template;
use crate::util::template::TemplateParams;
use rand::seq::SliceRandom;
use rand::Rng;
use std::collections::HashMap;

#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
pub enum Message<'a> {
    #[serde(borrow)]
    Single(Template<'a>),
    Choice(Vec<Template<'a>>),
}

impl<'a> Message<'a> {
    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),
        }
    }
}

#[derive(Deserialize, Debug, PartialEq, Eq)]
#[serde(untagged)]
enum NestedMap<'a> {
    #[serde(borrow)]
    Direct(Message<'a>),
    #[serde(borrow)]
    Nested(HashMap<&'a str, NestedMap<'a>>),
}

impl<'a> NestedMap<'a> {
    fn lookup(&'a self, path: &str) -> Option<&'a Message<'a>> {
        use NestedMap::*;
        let leaf =
            path.split(".")
                .fold(Some(self), |current, key| match current {
                    Some(Nested(m)) => m.get(key),
                    _ => None,
                });
        match leaf {
            Some(Direct(msg)) => Some(msg),
            _ => None,
        }
    }
}

#[cfg(test)]
mod nested_map_tests {
    use super::*;

    #[test]
    fn test_deserialize_nested_map() {
        let src = r#"
[global]
hello = "Hello World!"

[foo.bar]
single = "Single"
choice = ["Say this", "Or this"]
"#;
        let result = toml::from_str(src);
        assert_eq!(
            result,
            Ok(NestedMap::Nested(hashmap! {
                "global" => NestedMap::Nested(hashmap!{
                    "hello" => NestedMap::Direct(Message::Single(Template::parse("Hello World!").unwrap())),
                }),
                "foo" => NestedMap::Nested(hashmap!{
                    "bar" => NestedMap::Nested(hashmap!{
                        "single" => NestedMap::Direct(Message::Single(
                            Template::parse("Single").unwrap()
                        )),
                        "choice" => NestedMap::Direct(Message::Choice(
                            vec![
                                Template::parse("Say this").unwrap(),
                                Template::parse("Or this").unwrap()
                            ]
                        ))
                    })
                })
            }))
        )
    }

    #[test]
    fn test_lookup() {
        let map: NestedMap<'static> = toml::from_str(
            r#"
[global]
hello = "Hello World!"

[foo.bar]
single = "Single"
choice = ["Say this", "Or this"]
"#,
        )
        .unwrap();

        assert_eq!(
            map.lookup("global.hello"),
            Some(&Message::Single(Template::parse("Hello World!").unwrap()))
        );
        assert_eq!(
            map.lookup("foo.bar.single"),
            Some(&Message::Single(Template::parse("Single").unwrap()))
        );
        assert_eq!(
            map.lookup("foo.bar.choice"),
            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");
}

pub fn get<R: Rng + ?Sized>(
    name: &'static str,
    rng: &mut R,
) -> Option<&'static Template<'static>> {
    MESSAGES.lookup(name).and_then(|msg| msg.resolve(rng))
}

/// 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 get(name, 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);
            "Template Not Found".to_string()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand::rngs::SmallRng;
    use rand::SeedableRng;

    #[test]
    fn test_static_messages() {
        message(
            "global.welcome",
            &mut SmallRng::from_entropy(),
            &template_params!(),
        );
    }
}