use crate::{GameBoard, SavedGame, SokobanPlus};
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Puzzle {
pub board: GameBoard,
#[cfg_attr(feature = "serde", serde(default))]
pub title: String,
#[cfg_attr(feature = "serde", serde(default))]
pub author: String,
#[cfg_attr(feature = "serde", serde(default))]
pub notes: String,
#[cfg_attr(feature = "serde", serde(default))]
pub sokoban_plus: Option<SokobanPlus>,
#[cfg_attr(feature = "serde", serde(default))]
pub saved_games: Vec<SavedGame>,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(default)
)]
pub struct Collection {
pub title: String,
pub author: String,
pub created_at: String,
pub updated_at: String,
pub email: String,
pub url: String,
pub notes: String,
pub puzzles: Vec<Puzzle>,
}
impl Collection {
pub fn new() -> Self {
Self {
title: String::new(),
author: String::new(),
created_at: String::new(),
updated_at: String::new(),
email: String::new(),
url: String::new(),
notes: String::new(),
puzzles: Vec::new(),
}
}
}
#[cfg(all(test, feature = "serde"))]
mod tests {
use std::error::Error;
use super::*;
use crate::{BoxID, PlusID, Tessellation};
fn puzzle() -> Result<Puzzle, Box<dyn Error>> {
let mut sokoban_plus = SokobanPlus::new();
sokoban_plus.set_box_plus_id(BoxID::from(0_u8), Some(PlusID::new(1)?));
Ok(Puzzle {
board: GameBoard::parsed("#####|#@$.#|#####", Tessellation::Sokoban)?,
title: "Puzzle 1".to_string(),
author: "Tomislav".to_string(),
notes: "First one".to_string(),
saved_games: vec![SavedGame::parsed("[]LuR", Tessellation::Sokoban)?],
sokoban_plus: Some(sokoban_plus),
})
}
#[test]
fn it_round_trips_collections_through_json() -> Result<(), Box<dyn Error>> {
let mut collection = Collection::new();
collection.title = "Test collection".to_string();
collection.author = "Tomislav".to_string();
collection.puzzles = vec![
puzzle()?,
Puzzle {
board: GameBoard::parsed("---#-#-#-", Tessellation::Hexoban)?,
title: String::new(),
author: String::new(),
notes: String::new(),
saved_games: Vec::new(),
sokoban_plus: None,
},
];
let json = serde_json::to_string(&collection)?;
assert_eq!(serde_json::from_str::<Collection>(&json)?, collection);
Ok(())
}
#[test]
fn when_puzzle_has_only_a_board_the_rest_defaults() -> Result<(), Box<dyn Error>> {
let puzzle: Puzzle =
serde_json::from_str(r##"{"board": {"tessellation": "Sokoban", "board": "#@$.#"}}"##)?;
assert_eq!(puzzle.board.width(), 5);
assert!(puzzle.title.is_empty());
assert!(puzzle.saved_games.is_empty());
assert_eq!(puzzle.sokoban_plus, None);
Ok(())
}
#[test]
fn when_collection_is_empty_it_round_trips() -> Result<(), Box<dyn Error>> {
let collection: Collection = serde_json::from_str("{}")?;
assert_eq!(collection, Collection::new());
Ok(())
}
}