shared/domain/module/body/
matching.rs

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