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 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 })
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}