Skip to content

Commit

Permalink
Flatten Package structure
Browse files Browse the repository at this point in the history
Remove nested container struct for Round/Themes/Questions
  • Loading branch information
snpefk committed Sep 29, 2024
1 parent f9e0fc5 commit 5257c6f
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 73 deletions.
116 changes: 50 additions & 66 deletions crates/opensi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ use std::{fs::File, io, io::Read};
use zip::write::FileOptions;
use zip::{CompressionMethod, ZipArchive, ZipWriter};

pub mod serde_utils;
use serde_utils::*;

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename = "package")]
pub struct Package {
Expand All @@ -36,7 +39,8 @@ pub struct Package {

// elements
pub info: Info,
pub rounds: Rounds,
#[serde(deserialize_with = "unwrap_list", serialize_with = "wrap_round_list")]
pub rounds: Vec<Round>,
pub tags: Option<Vec<String>>,

// resources
Expand All @@ -47,37 +51,37 @@ pub struct Package {
impl Package {
/// Get [`Round`] by index.
pub fn get_round(&self, index: usize) -> Option<&Round> {
self.rounds.rounds.get(index)
self.rounds.get(index)
}

/// Get mutable [`Round`] by index.
pub fn get_round_mut(&mut self, index: usize) -> Option<&mut Round> {
self.rounds.rounds.get_mut(index)
self.rounds.get_mut(index)
}

/// Remove [`Round`] by index and return it.
pub fn remove_round(&mut self, index: usize) -> Option<Round> {
if index >= self.rounds.rounds.len() {
if index >= self.rounds.len() {
return None;
}
self.rounds.rounds.remove(index).into()
self.rounds.remove(index).into()
}

/// Push a new [`Round`] to the end of the package and
/// return a reference to it.
pub fn push_round(&mut self, round: Round) -> &mut Round {
self.rounds.rounds.push(round);
self.rounds.rounds.last_mut().unwrap()
self.rounds.push(round);
self.rounds.last_mut().unwrap()
}

/// Insert a new [`Round`] at position and return a
/// reference to it.
pub fn insert_round(&mut self, index: usize, round: Round) -> Option<&mut Round> {
if index > self.rounds.rounds.len() {
if index > self.rounds.len() {
return None;
}
self.rounds.rounds.insert(index, round);
Some(&mut self.rounds.rounds[index])
self.rounds.insert(index, round);
Some(&mut self.rounds[index])
}

/// Clone a [`Round`], push it afterwards and return
Expand All @@ -95,29 +99,29 @@ impl Package {

/// Get [`Theme`] in [`Round`] by indices.
pub fn get_theme(&self, round_index: usize, index: usize) -> Option<&Theme> {
self.get_round(round_index).and_then(|round| round.themes.themes.get(index))
self.get_round(round_index).and_then(|round| round.themes.get(index))
}

/// Get mutable [`Theme`] in [`Round`] by indices.
pub fn get_theme_mut(&mut self, round_index: usize, index: usize) -> Option<&mut Theme> {
self.get_round_mut(round_index).and_then(|round| round.themes.themes.get_mut(index))
self.get_round_mut(round_index).and_then(|round| round.themes.get_mut(index))
}

/// Remove [`Theme`] in [`Round`] by indices.
pub fn remove_theme(&mut self, round_index: usize, index: usize) -> Option<Theme> {
let round = self.get_round_mut(round_index)?;
if index >= round.themes.themes.len() {
if index >= round.themes.len() {
return None;
}
round.themes.themes.remove(index).into()
round.themes.remove(index).into()
}

/// Push a new [`Theme`] to the end of the [`Round`] and
/// return a reference to it.
pub fn push_theme(&mut self, round_index: usize, theme: Theme) -> Option<&mut Theme> {
let round = self.get_round_mut(round_index)?;
round.themes.themes.push(theme);
round.themes.themes.last_mut().unwrap().into()
round.themes.push(theme);
round.themes.last_mut().unwrap().into()
}

/// Insert a new [`Theme`] at position and return a
Expand All @@ -129,11 +133,11 @@ impl Package {
theme: Theme,
) -> Option<&mut Theme> {
let round = self.get_round_mut(round_index)?;
if index > round.themes.themes.len() {
if index > round.themes.len() {
return None;
}
round.themes.themes.insert(index, theme);
Some(&mut round.themes.themes[index])
round.themes.insert(index, theme);
Some(&mut round.themes[index])
}

/// Clone a [`Theme`], push it afterwards and return
Expand All @@ -158,8 +162,7 @@ impl Package {
theme_index: usize,
index: usize,
) -> Option<&Question> {
self.get_theme(round_index, theme_index)
.and_then(|theme| theme.questions.questions.get(index))
self.get_theme(round_index, theme_index).and_then(|theme| theme.questions.get(index))
}

/// Get mutable [`Question`] in [`Theme`] in [`Round`] by indices.
Expand All @@ -170,7 +173,7 @@ impl Package {
index: usize,
) -> Option<&mut Question> {
self.get_theme_mut(round_index, theme_index)
.and_then(|theme| theme.questions.questions.get_mut(index))
.and_then(|theme| theme.questions.get_mut(index))
}

/// Remove [`Question`] in [`Theme`] in [`Round`] by indices.
Expand All @@ -181,10 +184,10 @@ impl Package {
index: usize,
) -> Option<Question> {
let theme = self.get_theme_mut(round_index, theme_index)?;
if index >= theme.questions.questions.len() {
if index >= theme.questions.len() {
return None;
}
theme.questions.questions.remove(index).into()
theme.questions.remove(index).into()
}

/// Push a new [`Question`] to the end of the [`Theme`] in [`Round`]
Expand All @@ -196,8 +199,8 @@ impl Package {
question: Question,
) -> Option<&mut Question> {
let theme = self.get_theme_mut(round_index, theme_index)?;
theme.questions.questions.push(question);
theme.questions.questions.last_mut().unwrap().into()
theme.questions.push(question);
theme.questions.last_mut().unwrap().into()
}

/// Insert a new [`Question`] at position and return a
Expand All @@ -210,11 +213,11 @@ impl Package {
question: Question,
) -> Option<&mut Question> {
let theme = self.get_theme_mut(round_index, theme_index)?;
if index > theme.questions.questions.len() {
if index > theme.questions.len() {
return None;
}
theme.questions.questions.insert(index, question);
Some(&mut theme.questions.questions[index])
theme.questions.insert(index, question);
Some(&mut theme.questions[index])
}

/// Clone a [`question`], push it afterwards and return
Expand Down Expand Up @@ -255,7 +258,7 @@ impl Package {
let Some(theme) = self.get_theme(round_index, theme_index) else {
return 100;
};
let questions = &theme.questions.questions;
let questions = &theme.questions;
let mut iter = questions.iter().rev();
match (iter.next(), iter.next()) {
(Some(last), Some(prev)) => {
Expand Down Expand Up @@ -330,20 +333,16 @@ pub struct Round {
pub variant: Option<String>,
#[serde(rename = "@info")]
pub info: Option<Info>,
pub themes: Themes,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Themes {
#[serde(rename = "theme")]
#[serde(deserialize_with = "unwrap_list", serialize_with = "wrap_theme_list")]
pub themes: Vec<Theme>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Theme {
#[serde(rename = "@name")]
pub name: String,
pub questions: Questions,
#[serde(deserialize_with = "unwrap_list", serialize_with = "wrap_question_list")]
pub questions: Vec<Question>,
#[serde(rename = "@info")]
pub info: Option<Info>,
}
Expand All @@ -358,53 +357,38 @@ pub struct Questions {
pub struct Question {
#[serde(rename = "@price")]
pub price: usize,
pub scenario: Scenario,
pub right: Right,
pub wrong: Option<Wrong>,
#[serde(deserialize_with = "unwrap_list", serialize_with = "wrap_atom_list")]
pub scenario: Vec<Atom>,
#[serde(deserialize_with = "unwrap_list", serialize_with = "wrap_answer_list")]
pub right: Vec<Answer>,
#[serde(deserialize_with = "unwrap_option_list", serialize_with = "wrap_option_answer_list", default)]
pub wrong: Option<Vec<Answer>>,
#[serde(rename = "type")]
pub variant: Option<Variant>,
pub question_type: Option<QuestionType>,
#[serde(rename = "@info")]
pub info: Option<Info>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Variant {
pub struct QuestionType {
#[serde(rename = "@name")]
pub name: String,
#[serde(rename = "param")]
pub params: Option<Vec<Param>>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Param {
#[serde(rename = "@name")]
pub name: String,
pub struct Answer {
#[serde(rename = "$value")]
pub body: String,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Scenario {
#[serde(rename = "atom")]
pub atoms: Vec<Atom>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Right {
#[serde(rename = "answer")]
pub answers: Vec<Answer>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Wrong {
#[serde(rename = "answer")]
pub answers: Vec<Answer>,
pub body: Option<String>,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
pub struct Answer {
pub struct Param {
#[serde(rename = "@name")]
pub name: String,
#[serde(rename = "$value")]
pub body: Option<String>,
pub body: String,
}

#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
Expand Down
109 changes: 109 additions & 0 deletions crates/opensi-core/src/serde_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::{Answer, Atom, Question, Round, Theme};

// Generic function to deserialize List structures
pub fn unwrap_list<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Default,
{
#[derive(Deserialize)]
struct List<T> {
#[serde(rename = "$value")]
element: Vec<T>,
}
Ok(List::deserialize(deserializer)?.element)
}

pub fn unwrap_option_list<'de, T, D>(deserializer: D) -> Result<Option<Vec<T>>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Default,
{
#[derive(Serialize, Deserialize)]
struct List<T> {
// #[serde(default)]
#[serde(rename = "$value")]
element: Option<Vec<T>>,
}

Ok(List::deserialize(deserializer)?.element)
}

pub fn wrap_round_list<S>(rounds: &Vec<Round>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
round: &'a Vec<Round>,
}

let list = List { round: rounds };
list.serialize(serializer)
}

pub fn wrap_theme_list<S>(themes: &Vec<Theme>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
theme: &'a Vec<Theme>,
}

let list = List { theme: themes };
list.serialize(serializer)
}

pub fn wrap_question_list<S>(questions: &Vec<Question>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
question: &'a Vec<Question>,
}

let list = List { question: questions };
list.serialize(serializer)
}

pub fn wrap_atom_list<S>(atoms: &Vec<Atom>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
atom: &'a Vec<Atom>,
}

let list = List { atom: atoms };
list.serialize(serializer)
}

pub fn wrap_answer_list<S>(answers: &Vec<Answer>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
answer: &'a Vec<Answer>,
}

let list = List { answer: answers };
list.serialize(serializer)
}

pub fn wrap_option_answer_list<S>(answers: &Option<Vec<Answer>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
#[derive(Serialize)]
struct List<'a> {
answer: &'a Option<Vec<Answer>>,
}

let list = List { answer: answers };
list.serialize(serializer)
}
Loading

0 comments on commit 5257c6f

Please sign in to comment.