hangman_solver_lib/
lib.rs

1// SPDX-License-Identifier: EUPL-1.2
2#![warn(
3    clippy::missing_const_for_fn,
4    clippy::nursery,
5    clippy::pedantic,
6    clippy::todo
7)]
8#![deny(clippy::indexing_slicing, clippy::panic, clippy::unwrap_used)]
9#![allow(clippy::missing_errors_doc, clippy::option_if_let_else)]
10#![deny(unsafe_code)]
11mod language;
12mod solver;
13
14pub use crate::solver::{
15    CharCollection, HangmanResult, InfallibleCharCollection, Pattern,
16};
17
18pub use crate::language::{Language, StringChunkIter, WordSequence};
19
20#[cfg(feature = "wasm-bindgen")]
21pub use crate::solver::WasmHangmanResult;
22#[cfg(feature = "wasm-bindgen")]
23use js_sys::JsString;
24#[cfg(feature = "pyo3")]
25use pyo3::types::PyString;
26#[cfg(feature = "wasm-bindgen")]
27use wasm_bindgen::prelude::*;
28
29#[cfg(feature = "pyo3")]
30pub use crate::language::UnknownLanguageError;
31#[cfg(feature = "pyo3")]
32use pyo3::prelude::*;
33
34#[cfg(feature = "pyo3")]
35#[derive(FromPyObject)]
36pub enum InvalidLetters<'py> {
37    String(Bound<'py, PyString>),
38    Chars(Vec<char>),
39}
40
41#[cfg(feature = "pyo3")]
42#[pyfunction]
43#[pyo3(signature = (pattern_string, invalid_letters, language, max_words_to_collect))]
44#[allow(clippy::needless_pass_by_value)]
45pub fn solve(
46    pattern_string: Bound<'_, PyString>,
47    invalid_letters: InvalidLetters<'_>,
48    language: Language,
49    max_words_to_collect: usize,
50) -> PyResult<HangmanResult> {
51    match invalid_letters {
52        InvalidLetters::String(invalid_letters) => crate::solver::solve(
53            &pattern_string,
54            &invalid_letters,
55            true,
56            language,
57            Some(max_words_to_collect),
58        ),
59        InvalidLetters::Chars(invalid_letters) => crate::solver::solve(
60            &pattern_string,
61            &invalid_letters,
62            true,
63            language,
64            Some(max_words_to_collect),
65        ),
66    }
67}
68
69#[cfg(feature = "pyo3")]
70#[pyfunction]
71#[pyo3(signature = (pattern_string, invalid_letters, language, max_words_to_collect))]
72#[allow(clippy::needless_pass_by_value)]
73pub fn solve_crossword(
74    pattern_string: Bound<'_, PyString>,
75    invalid_letters: InvalidLetters<'_>,
76    language: Language,
77    max_words_to_collect: usize,
78) -> PyResult<HangmanResult> {
79    match invalid_letters {
80        InvalidLetters::String(invalid_letters) => crate::solver::solve(
81            &pattern_string,
82            &invalid_letters,
83            false,
84            language,
85            Some(max_words_to_collect),
86        ),
87        InvalidLetters::Chars(invalid_letters) => crate::solver::solve(
88            &pattern_string,
89            &invalid_letters,
90            false,
91            language,
92            Some(max_words_to_collect),
93        ),
94    }
95}
96
97#[must_use]
98#[cfg(feature = "pyo3")]
99#[pyfunction]
100#[pyo3(signature = (language, word_length))]
101pub const fn read_words_with_length(
102    language: Language,
103    word_length: usize,
104) -> WordSequence {
105    language.read_words(word_length)
106}
107
108#[cfg(feature = "pyo3")]
109#[pymodule]
110#[pyo3(name = "_solver")]
111pub fn hangman_solver(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
112    m.add_function(wrap_pyfunction!(solve, m)?)?;
113    m.add_function(wrap_pyfunction!(solve_crossword, m)?)?;
114    m.add_function(wrap_pyfunction!(read_words_with_length, m)?)?;
115    m.add(
116        "UnknownLanguageError",
117        py.get_type::<UnknownLanguageError>(),
118    )?;
119    m.add_class::<HangmanResult>()?;
120    m.add_class::<Language>()?;
121    Ok(())
122}
123
124#[cfg(feature = "wasm-bindgen")]
125#[wasm_bindgen]
126#[allow(clippy::needless_pass_by_value)]
127pub fn solve_hangman(
128    all_words: Vec<JsString>,
129    pattern_string: JsString,
130    invalid_letters: JsString,
131    max_words_to_collect: usize,
132    crossword_mode: bool,
133) -> Result<WasmHangmanResult, JsValue> {
134    use crate::solver::solve_js;
135
136    Ok(solve_js(
137        &mut all_words.iter(),
138        &pattern_string,
139        &invalid_letters,
140        Some(max_words_to_collect),
141        crossword_mode,
142    ))
143}