puz_parse/
lib.rs

1//! A library for parsing .puz crossword puzzle files.
2//!
3//! This library provides functionality to parse the binary .puz file format
4//! used by crossword puzzle applications in the early-mid 2000s, like AcrossLite.
5//! It supports all standard puzzle features including rebus squares, circled squares,
6//! and various puzzle extensions.
7//!
8//! # Quick Start
9//!
10//! ```rust,no_run
11//! use puz_parse::parse_file;
12//!
13//! let puzzle = parse_file("puzzle.puz")?;
14//! println!("Title: {}", puzzle.info.title);
15//! println!("Size: {}x{}", puzzle.info.width, puzzle.info.height);
16//! # Ok::<(), Box<dyn std::error::Error>>(())
17//! ```
18//!
19//! # Advanced Usage
20//!
21//! For more control over parsing and error handling:
22//!
23//! ```rust,no_run
24//! use std::fs::File;
25//! use puz_parse::parse;
26//!
27//! let file = File::open("puzzle.puz")?;
28//! let result = parse(file)?;
29//! let puzzle = result.result;
30//!
31//! // Handle any warnings that occurred during parsing
32//! for warning in &result.warnings {
33//!     eprintln!("Warning: {}", warning);
34//! }
35//! # Ok::<(), Box<dyn std::error::Error>>(())
36//! ```
37//!
38//! # Features
39//!
40//! - **Complete .puz parsing**: Supports all standard puzzle features
41//! - **Error recovery**: Continues parsing with warnings for non-critical issues  
42//! - **Memory efficient**: Zero-copy parsing where possible
43//! - **Extensible**: Handles rebus squares, circles, and other puzzle extensions
44//! - **JSON support**: Optional serde support via the `json` feature
45//!
46//! # Optional Features
47//!
48//! - `json`: Enables JSON serialization support via serde
49
50mod error;
51mod parser;
52mod types;
53
54pub use error::{ParseResult, PuzError, PuzWarning};
55pub use types::*;
56
57use std::io::Read;
58use std::path::Path;
59
60/// Parse a .puz file from any source that implements `Read`.
61///
62/// This is the core parsing function that provides full control over error handling
63/// and warnings. Use [`parse_file`] for a simpler API when parsing from files.
64///
65/// # Arguments
66///
67/// * `reader` - Any type that implements `Read`, such as a `File` or `&[u8]`
68///
69/// # Returns
70///
71/// Returns a `Result<ParseResult<Puzzle>, PuzError>` containing the parsed puzzle data
72/// along with any warnings, or an error if parsing fails.
73///
74/// # Example
75///
76/// ```rust,no_run
77/// use std::fs::File;
78/// use puz_parse::parse;
79///
80/// let file = File::open("puzzle.puz")?;
81/// let result = parse(file)?;
82/// let puzzle = result.result;
83/// for warning in &result.warnings {
84///     eprintln!("Warning: {}", warning);
85/// }
86/// # Ok::<(), Box<dyn std::error::Error>>(())
87/// ```
88pub fn parse<R: Read>(reader: R) -> Result<ParseResult<Puzzle>, PuzError> {
89    parser::parse_puzzle(reader)
90}
91
92/// Parse a .puz file from a file path.
93///
94/// This is a convenience function that handles file opening and returns just the
95/// puzzle data. Warnings are discarded. Use [`parse`] for full control.
96///
97/// # Arguments
98///
99/// * `path` - Path to the .puz file
100///
101/// # Returns
102///
103/// Returns the parsed `Puzzle` or an error if parsing fails.
104///
105/// # Example
106///
107/// ```rust,no_run
108/// use puz_parse::parse_file;
109///
110/// let puzzle = parse_file("puzzle.puz")?;
111/// println!("Puzzle: {} by {}", puzzle.info.title, puzzle.info.author);
112/// # Ok::<(), Box<dyn std::error::Error>>(())
113/// ```
114pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Puzzle, PuzError> {
115    let file = std::fs::File::open(path.as_ref()).map_err(|e| PuzError::IoError {
116        message: format!("Failed to open file: {e}"),
117        kind: e.kind(),
118        position: None,
119    })?;
120
121    let result = parse(file)?;
122    Ok(result.result)
123}
124
125/// Parse a .puz file from a byte slice.
126///
127/// Convenience function for parsing puzzle data already in memory.
128///
129/// # Arguments
130///
131/// * `data` - Byte slice containing .puz file data
132///
133/// # Returns
134///
135/// Returns the parsed `Puzzle` or an error if parsing fails.
136///
137/// # Example
138///
139/// ```rust,no_run
140/// use puz_parse::parse_bytes;
141///
142/// let data = std::fs::read("puzzle.puz")?;
143/// let puzzle = parse_bytes(&data)?;
144/// # Ok::<(), Box<dyn std::error::Error>>(())
145/// ```
146pub fn parse_bytes(data: &[u8]) -> Result<Puzzle, PuzError> {
147    let result = parse(data)?;
148    Ok(result.result)
149}