puz_parse/types.rs
1use std::collections::HashMap;
2
3/// A complete crossword puzzle parsed from a .puz file.
4///
5/// This is the main data structure returned by the parsing functions.
6/// It contains all the information needed to display and interact with
7/// a crossword puzzle.
8///
9/// # Examples
10///
11/// ```rust,no_run
12/// use puz_parse::parse_file;
13///
14/// let puzzle = parse_file("puzzle.puz")?;
15/// println!("Title: {}", puzzle.info.title);
16/// println!("Grid size: {}x{}", puzzle.info.width, puzzle.info.height);
17/// println!("Number of across clues: {}", puzzle.clues.across.len());
18/// # Ok::<(), Box<dyn std::error::Error>>(())
19/// ```
20#[derive(Debug, Clone, PartialEq)]
21#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
22pub struct Puzzle {
23 /// Basic puzzle metadata (title, author, dimensions, etc.)
24 pub info: PuzzleInfo,
25 /// The puzzle grid (solution and blank grids)
26 pub grid: Grid,
27 /// Clues organized by direction and number
28 pub clues: Clues,
29 /// Optional puzzle extensions (rebus, circles, etc.)
30 pub extensions: Extensions,
31}
32
33/// Basic information about the puzzle.
34///
35/// Contains metadata like title, author, dimensions, and format information.
36#[derive(Debug, Clone, PartialEq)]
37#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
38pub struct PuzzleInfo {
39 /// Puzzle title
40 pub title: String,
41 /// Author name(s)
42 pub author: String,
43 /// Copyright information
44 pub copyright: String,
45 /// Additional notes or instructions
46 pub notes: String,
47 /// Grid width (number of columns)
48 pub width: u8,
49 /// Grid height (number of rows)
50 pub height: u8,
51 /// File format version
52 pub version: String,
53 /// Whether the puzzle solution is scrambled
54 pub is_scrambled: bool,
55}
56
57/// The puzzle grid containing both solution and blank layouts.
58///
59/// The grid is represented as vectors of strings, where each string is a row.
60/// Characters represent:
61/// - `.` = black/blocked square
62/// - `-` = empty square (in blank grid)
63/// - Letters/numbers = cell content
64#[derive(Debug, Clone, PartialEq)]
65#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
66pub struct Grid {
67 /// The blank grid as presented to the solver (with `-` for empty squares)
68 pub blank: Vec<String>,
69 /// The solution grid with all answers filled in
70 pub solution: Vec<String>,
71}
72
73/// Clues organized by direction and number.
74///
75/// Clue numbers correspond to the starting squares in the grid.
76/// The ordering follows standard crossword conventions.
77#[derive(Debug, Clone, PartialEq)]
78#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
79pub struct Clues {
80 /// Across clues mapped by clue number
81 pub across: HashMap<u16, String>,
82 /// Down clues mapped by clue number
83 pub down: HashMap<u16, String>,
84}
85
86/// Optional puzzle extensions for advanced features.
87///
88/// These fields contain additional puzzle information like rebus squares
89/// (multi-letter cells), circled squares, and given squares.
90#[derive(Debug, Clone, PartialEq)]
91#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
92pub struct Extensions {
93 /// Rebus squares (cells with multiple letters), if present
94 pub rebus: Option<Rebus>,
95 /// Grid indicating which squares are circled, if any
96 pub circles: Option<Vec<Vec<bool>>>,
97 /// Grid indicating which squares were given to the solver, if any
98 pub given: Option<Vec<Vec<bool>>>,
99}
100
101/// Rebus information for squares containing multiple letters.
102///
103/// A rebus allows a single square to contain multiple letters or words.
104/// The grid indicates which squares are rebus squares, and the table
105/// maps rebus keys to their string values.
106#[derive(Debug, Clone, PartialEq)]
107#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
108pub struct Rebus {
109 /// Grid indicating rebus keys (0 = no rebus, 1-255 = rebus key)
110 pub grid: Vec<Vec<u8>>,
111 /// Mapping of rebus keys to their string values
112 pub table: HashMap<u8, String>,
113}
114
115pub(crate) const FREE_SQUARE: char = '-';
116pub(crate) const TAKEN_SQUARE: char = '.';