shared/domain/module/body/
cover.rs

1use crate::domain::module::{
2    body::{Body, BodyConvert, BodyExt, StepExt, ThemeId, _groups::design::*},
3    ModuleKind,
4};
5use serde::{de::IntoDeserializer, Deserialize, Serialize};
6use std::collections::HashSet;
7
8use super::Audio;
9
10/// The body for [`Cover`](crate::domain::module::ModuleKind::Cover) modules.
11#[derive(Clone, Serialize, Deserialize, Debug, Default)]
12pub struct ModuleData {
13    /// The content
14    pub content: Option<Content>,
15}
16
17impl BodyExt<(), Step> for ModuleData {
18    fn as_body(&self) -> Body {
19        Body::Cover(self.clone())
20    }
21
22    fn is_complete(&self) -> bool {
23        self.content.is_some()
24    }
25
26    fn kind() -> ModuleKind {
27        ModuleKind::Cover
28    }
29
30    fn new_with_mode_and_theme(_mode: (), theme: ThemeId) -> Self {
31        ModuleData {
32            content: Some(Content {
33                base: BaseContent {
34                    theme,
35                    ..Default::default()
36                },
37                ..Default::default()
38            }),
39            ..Default::default()
40        }
41    }
42
43    fn mode(&self) -> Option<()> {
44        None
45    }
46
47    fn requires_choose_mode(&self) -> bool {
48        false
49    }
50
51    fn set_editor_state_step(&mut self, step: Step) {
52        if let Some(content) = self.content.as_mut() {
53            content.editor_state.step = step;
54        }
55    }
56    fn set_editor_state_steps_completed(&mut self, steps_completed: HashSet<Step>) {
57        if let Some(content) = self.content.as_mut() {
58            content.editor_state.steps_completed = steps_completed;
59        }
60    }
61
62    fn get_editor_state_step(&self) -> Option<Step> {
63        self.content
64            .as_ref()
65            .map(|content| content.editor_state.step)
66    }
67
68    fn get_editor_state_steps_completed(&self) -> Option<HashSet<Step>> {
69        self.content
70            .as_ref()
71            .map(|content| content.editor_state.steps_completed.clone())
72    }
73
74    fn set_theme(&mut self, theme_id: ThemeId) {
75        if let Some(content) = self.content.as_mut() {
76            content.base.theme = theme_id;
77        }
78    }
79
80    fn get_theme(&self) -> Option<ThemeId> {
81        self.content.as_ref().map(|content| content.base.theme)
82    }
83}
84
85impl BodyConvert for ModuleData {}
86
87impl TryFrom<Body> for ModuleData {
88    type Error = &'static str;
89
90    fn try_from(body: Body) -> Result<Self, Self::Error> {
91        match body {
92            Body::Cover(data) => Ok(data),
93            _ => Err("cannot convert body to cover!"),
94        }
95    }
96}
97
98/// The body for [`Cover`](crate::domain::module::ModuleKind::Cover) modules.
99#[derive(Default, Clone, Serialize, Deserialize, Debug)]
100pub struct Content {
101    /// The editor state
102    pub editor_state: EditorState,
103    /// The base content for all design modules
104    pub base: BaseContent,
105    /// Optional audio for the activity
106    #[serde(default)]
107    pub audio: Option<Audio>,
108    /// play settings
109    #[serde(default)]
110    pub play_settings: PlaySettings,
111}
112
113/// Editor state
114#[derive(Default, Clone, Serialize, Deserialize, Debug)]
115pub struct EditorState {
116    /// the current step
117    pub step: Step,
118
119    /// the completed steps
120    pub steps_completed: HashSet<Step>,
121}
122
123// TODO Currently there exists some Cover modules with an editor_state which has the step set to
124// Four, or Four in the steps_completed field. The workaround here is to tell serde to make use of
125// the custom Deserialize implementation (and Serialize) by using itself as a remote type.
126// See https://serde.rs/remote-derive.html
127
128/// The Steps
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
130#[serde(remote = "Step")]
131pub enum Step {
132    /// Step 1
133    One,
134    /// Step 2
135    Two,
136    /// Step 3
137    Three,
138    /// Step 4
139    Four,
140}
141
142impl<'de> Deserialize<'de> for Step {
143    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
144    where
145        D: serde::Deserializer<'de>,
146    {
147        let value = String::deserialize(deserializer)?;
148        if value == "Four" {
149            Ok(Self::Three)
150        } else {
151            Step::deserialize(value.into_deserializer())
152        }
153    }
154}
155
156impl Serialize for Step {
157    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
158    where
159        S: serde::Serializer,
160    {
161        Step::serialize(&self, serializer)
162    }
163}
164
165impl Default for Step {
166    fn default() -> Self {
167        Self::One
168    }
169}
170
171impl StepExt for Step {
172    fn next(&self) -> Option<Self> {
173        match self {
174            Self::One => Some(Self::Two),
175            Self::Two => Some(Self::Three),
176            Self::Three => Some(Self::Four),
177            Self::Four => None,
178        }
179    }
180
181    fn as_number(&self) -> usize {
182        match self {
183            Self::One => 1,
184            Self::Two => 2,
185            Self::Three => 3,
186            Self::Four => 4,
187        }
188    }
189
190    fn label(&self) -> &'static str {
191        const STR_DESIGN: &'static str = "Design";
192        const STR_CONTENT: &'static str = "Content";
193        const STR_SETTINGS: &'static str = "Settings";
194        const STR_PREVIEW: &'static str = "Preview";
195
196        match self {
197            Self::One => STR_DESIGN,
198            Self::Two => STR_CONTENT,
199            Self::Three => STR_SETTINGS,
200            Self::Four => STR_PREVIEW,
201        }
202    }
203
204    fn get_list() -> Vec<Self> {
205        vec![Self::One, Self::Two, Self::Three, Self::Four]
206    }
207    fn get_preview() -> Self {
208        Self::Four
209    }
210}
211
212/// Play settings
213#[derive(Clone, Default, Serialize, Deserialize, Debug)]
214pub struct PlaySettings {
215    /// next style
216    pub next: Next,
217}
218
219/// Next
220#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq, Eq)]
221pub enum Next {
222    /// System will determine the default option
223    Auto,
224    /// After audio has played
225    AfterAudio,
226    /// Student clicks next
227    ClickNext,
228}
229
230impl Default for Next {
231    fn default() -> Self {
232        Self::Auto
233    }
234}