shared/domain/module/body/
card_quiz.rs

1use crate::domain::module::{
2    body::{Body, BodyConvert, BodyExt, ThemeId, _groups::cards::*},
3    ModuleKind,
4};
5use serde::{Deserialize, Serialize};
6use std::collections::HashSet;
7
8/// The body for [`CardQuiz`](crate::domain::module::ModuleKind::CardQuiz) modules.
9#[derive(Default, Clone, Serialize, Deserialize, Debug)]
10pub struct ModuleData {
11    /// The content
12    pub content: Option<Content>,
13}
14
15/// The content for [`CardQuiz`](crate::domain::module::ModuleKind::CardQuiz) modules.
16#[derive(Default, Clone, Serialize, Deserialize, Debug)]
17pub struct Content {
18    /// The base content for all cards modules
19    pub base: BaseContent,
20    /// Settings for playback
21    pub player_settings: PlayerSettings,
22}
23
24/// Player settings
25#[derive(Clone, Serialize, Deserialize, Debug)]
26pub struct PlayerSettings {
27    /// number of choices
28    pub n_choices: u8,
29
30    /// swap the display to be primary left vs. right
31    pub swap: bool,
32
33    /// number of rounds to play
34    pub n_rounds: u32,
35
36    /// time limit in minutes
37    pub time_limit: Option<u32>,
38}
39
40impl Default for PlayerSettings {
41    fn default() -> Self {
42        Self {
43            n_choices: 3,
44            swap: false,
45            n_rounds: 3,
46            time_limit: None,
47        }
48    }
49}
50/// Module is a memory game, and has a memory game's body.
51impl BodyExt<Mode, Step> for ModuleData {
52    fn as_body(&self) -> Body {
53        Body::CardQuiz(self.clone())
54    }
55
56    fn is_complete(&self) -> bool {
57        self.content
58            .as_ref()
59            .map_or(false, |content| content.base.is_valid())
60    }
61
62    fn kind() -> ModuleKind {
63        ModuleKind::CardQuiz
64    }
65
66    fn new_with_mode_and_theme(mode: Mode, theme: ThemeId) -> Self {
67        ModuleData {
68            content: Some(Content {
69                base: BaseContent {
70                    mode,
71                    theme,
72                    ..Default::default()
73                },
74                ..Default::default()
75            }),
76        }
77    }
78
79    fn mode(&self) -> Option<Mode> {
80        self.content.as_ref().map(|c| c.base.mode.clone())
81    }
82
83    fn requires_choose_mode(&self) -> bool {
84        self.content.is_none()
85    }
86
87    fn set_editor_state_step(&mut self, step: Step) {
88        if let Some(content) = self.content.as_mut() {
89            content.base.editor_state.step = step;
90        }
91    }
92    fn set_editor_state_steps_completed(&mut self, steps_completed: HashSet<Step>) {
93        if let Some(content) = self.content.as_mut() {
94            content.base.editor_state.steps_completed = steps_completed;
95        }
96    }
97
98    fn get_editor_state_step(&self) -> Option<Step> {
99        self.content
100            .as_ref()
101            .map(|content| content.base.editor_state.step)
102    }
103
104    fn get_editor_state_steps_completed(&self) -> Option<HashSet<Step>> {
105        self.content
106            .as_ref()
107            .map(|content| content.base.editor_state.steps_completed.clone())
108    }
109
110    fn set_theme(&mut self, theme_id: ThemeId) {
111        if let Some(content) = self.content.as_mut() {
112            content.base.theme = theme_id;
113        }
114    }
115
116    fn get_theme(&self) -> Option<ThemeId> {
117        self.content.as_ref().map(|content| content.base.theme)
118    }
119}
120
121impl BodyConvert for ModuleData {
122    fn convertable_list() -> Vec<ModuleKind> {
123        vec![
124            ModuleKind::Memory,
125            ModuleKind::Matching,
126            ModuleKind::Flashcards,
127        ]
128    }
129
130    fn convert_to_memory(&self) -> Result<super::memory::ModuleData, &'static str> {
131        Ok(super::memory::ModuleData {
132            content: self.content.as_ref().map(|content| super::memory::Content {
133                base: content.base.clone(),
134                player_settings: super::memory::PlayerSettings::default(),
135            }),
136        })
137    }
138    fn convert_to_matching(&self) -> Result<super::matching::ModuleData, &'static str> {
139        Ok(super::matching::ModuleData {
140            content: self
141                .content
142                .as_ref()
143                .map(|content| super::matching::Content {
144                    base: content.base.clone(),
145                    player_settings: super::matching::PlayerSettings::default(),
146                }),
147        })
148    }
149
150    fn convert_to_flashcards(&self) -> Result<super::flashcards::ModuleData, &'static str> {
151        Ok(super::flashcards::ModuleData {
152            content: self
153                .content
154                .as_ref()
155                .map(|content| super::flashcards::Content {
156                    base: content.base.clone(),
157                    player_settings: super::flashcards::PlayerSettings::default(),
158                }),
159        })
160    }
161}
162
163impl TryFrom<Body> for ModuleData {
164    type Error = &'static str;
165
166    fn try_from(body: Body) -> Result<Self, Self::Error> {
167        match body {
168            Body::CardQuiz(data) => Ok(data),
169            _ => Err("cannot convert body to flashcards!"),
170        }
171    }
172}