good_morning/
lib.rs

1use std::{collections::HashMap, sync::Arc};
2
3use modifier::Modifier;
4use rand::seq::IndexedRandom;
5use template::{Template, TemplateFlags, TemplateFlagsTable};
6
7pub mod error;
8mod loader;
9pub mod template;
10
11pub mod modifier;
12
13pub use error::{GoodMorningError, Result};
14
15#[derive(Debug, Clone, Default)]
16pub struct GoodMorning {
17    pub templates: Vec<Template>,
18    pub table: TemplateFlagsTable,
19    pub modifiers: HashMap<&'static str, Arc<Box<dyn Modifier>>>,
20}
21
22impl GoodMorning {
23    /// Creates a new instance of `GoodMorning` with default settings and registers default modifiers.
24    pub fn new() -> Self {
25        let mut this = Self::default();
26
27        this.register_static_modifiers(Self::DEFAULT_MODIFIERS);
28
29        this
30    }
31
32    fn get_flags(&self, args: &HashMap<String, String>) -> TemplateFlags {
33        self.table
34            .get_flags(args.keys().map(|k| k.as_str()).collect())
35    }
36
37    fn choose_template<'a>(&self, templates: &Vec<&'a Template>) -> &'a Template {
38        templates
39            .choose_weighted(&mut rand::rng(), |template| {
40                (template.flags.count_ones()).pow(2) * 10 + 1 // To avoid zero weight
41            })
42            .expect("No template found matching the given flags")
43    }
44
45    pub fn execute(&self, args: &HashMap<String, String>) -> Result<String> {
46        let flags = self.get_flags(args);
47        let templates = self.templates
48            .iter()
49            .filter(|template| {
50                let masks = template.flags();
51                masks == 0 || flags & masks == masks
52            })
53            .collect();
54
55        let template = self.choose_template(&templates);
56        template.content(args)
57    }
58
59    pub fn execute_with_required(
60        &self,
61        args: &HashMap<String, String>,
62        required: Vec<String>,
63    ) -> Result<String> {
64        let flags = self.get_flags(args);
65        let required_masks = self
66            .table
67            .get_flags(required.iter().map(|s| s.as_str()).collect());
68
69        let templates: Vec<&Template> = self.templates
70            .iter()
71            .filter(|template| {
72                let masks = template.flags();
73                masks & required_masks == required_masks && flags & masks == masks
74            })
75            .collect();
76        println!("Found {} templates matching required flags", templates.len());
77
78        let template = self.choose_template(&templates);
79        template.content(args)
80    }
81}
82
83#[cfg(test)]
84mod test {
85    use super::*;
86
87    fn setup_hearts_echo() -> GoodMorning {
88        let mut good_morning = GoodMorning::new();
89        good_morning.load_template("Hello, {name}!").unwrap();
90        good_morning
91    }
92
93    #[test]
94    fn test_execute() {
95        let good_morning = setup_hearts_echo();
96        let args = HashMap::from([("name".to_string(), "World".to_string())]);
97        let result = good_morning.execute(&args).unwrap();
98        assert_eq!(result, "Hello, World!");
99    }
100
101    #[test]
102    fn test_execute_with_required() {
103        let good_morning = setup_hearts_echo();
104        let args = HashMap::from([("name".to_string(), "Alice".to_string())]);
105        let result = good_morning.execute_with_required(&args, vec![]).unwrap();
106        assert_eq!(result, "Hello, Alice!");
107    }
108}