the api surface
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use cruciverb_core::grid::Cell;
|
||||
use cruciverb_core::puzzle::Puzzle;
|
||||
|
||||
// -- request types --
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GenerateRequest {
|
||||
pub difficulty: Option<String>,
|
||||
pub size: Option<usize>,
|
||||
pub density: Option<f64>,
|
||||
pub clue_difficulty: Option<u8>,
|
||||
pub seed: Option<u64>,
|
||||
pub theme: Option<String>,
|
||||
pub cryptic: Option<bool>,
|
||||
pub pack: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ValidateRequest {
|
||||
pub cells: Vec<Vec<Option<String>>>,
|
||||
pub elapsed: Option<i64>,
|
||||
// kept for client compatibility; the server now uses its own clock for streaks
|
||||
#[allow(dead_code)]
|
||||
pub local_date: Option<String>,
|
||||
pub room_code: Option<String>,
|
||||
pub cell_results: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct HintRequest {
|
||||
pub kind: String,
|
||||
pub row: usize,
|
||||
pub col: usize,
|
||||
pub direction: Option<String>,
|
||||
pub letter: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ProgressRequest {
|
||||
pub cells: Vec<Vec<Option<String>>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct UpdateMeRequest {
|
||||
pub display_name: Option<String>,
|
||||
}
|
||||
|
||||
// -- response types --
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct GenerateResponse {
|
||||
pub id: String,
|
||||
pub short_id: String,
|
||||
pub puzzle: ClientPuzzle,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ClientPuzzleResponse {
|
||||
pub id: String,
|
||||
pub short_id: String,
|
||||
pub puzzle: ClientPuzzle,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ClientPuzzle {
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub cells: Vec<Vec<ClientCell>>,
|
||||
pub clues_across: Vec<ClientClue>,
|
||||
pub clues_down: Vec<ClientClue>,
|
||||
pub difficulty_score: u32,
|
||||
pub cell_numbers: HashMap<String, u32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub theme: Option<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub theme_entries: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ClientCell {
|
||||
Black,
|
||||
Empty,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ClientClue {
|
||||
pub number: u32,
|
||||
pub direction: String,
|
||||
pub text: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub definition: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub wordplay_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum HintResponse {
|
||||
CheckCell { is_correct: bool },
|
||||
RevealLetter { row: usize, col: usize, letter: String },
|
||||
RevealWord { letters: Vec<LetterPosition> },
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LetterPosition {
|
||||
pub row: usize,
|
||||
pub col: usize,
|
||||
pub letter: String,
|
||||
}
|
||||
|
||||
// -- conversions --
|
||||
|
||||
pub fn to_client_puzzle(puzzle: &Puzzle, _id: Uuid) -> ClientPuzzle {
|
||||
let mut cells = Vec::with_capacity(puzzle.height);
|
||||
for r in 0..puzzle.height {
|
||||
let mut row = Vec::with_capacity(puzzle.width);
|
||||
for c in 0..puzzle.width {
|
||||
match puzzle.grid.get(r, c) {
|
||||
Cell::Black => row.push(ClientCell::Black),
|
||||
_ => row.push(ClientCell::Empty),
|
||||
}
|
||||
}
|
||||
cells.push(row);
|
||||
}
|
||||
|
||||
let mut cell_numbers: HashMap<String, u32> = HashMap::new();
|
||||
for word in &puzzle.words {
|
||||
let key = format!("{},{}", word.row, word.col);
|
||||
cell_numbers.entry(key).or_insert(word.clue_number);
|
||||
}
|
||||
|
||||
let clues_across = puzzle.clues_across.iter().map(|c| ClientClue {
|
||||
number: c.number,
|
||||
direction: "across".into(),
|
||||
text: c.text.clone(),
|
||||
definition: c.definition.clone(),
|
||||
wordplay_type: c.wordplay_type.clone(),
|
||||
}).collect();
|
||||
|
||||
let clues_down = puzzle.clues_down.iter().map(|c| ClientClue {
|
||||
number: c.number,
|
||||
direction: "down".into(),
|
||||
text: c.text.clone(),
|
||||
definition: c.definition.clone(),
|
||||
wordplay_type: c.wordplay_type.clone(),
|
||||
}).collect();
|
||||
|
||||
ClientPuzzle {
|
||||
width: puzzle.width,
|
||||
height: puzzle.height,
|
||||
cells,
|
||||
clues_across,
|
||||
clues_down,
|
||||
difficulty_score: puzzle.difficulty_score,
|
||||
cell_numbers,
|
||||
theme: puzzle.theme.clone(),
|
||||
theme_entries: puzzle.theme_entries.clone(),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user