hangman_solver_lib/solver/
hangman_result.rs1use cfg_if::cfg_if;
4use std::fmt::Display;
5
6use crate::Language;
7use crate::solver::infallible_char_collection::InfallibleCharCollection as _;
8
9#[cfg(feature = "pyo3")]
10use pyo3::prelude::*;
11
12#[cfg(feature = "wasm-bindgen")]
13use js_sys::JsString;
14#[cfg(feature = "wasm-bindgen")]
15use wasm_bindgen::prelude::*;
16
17#[inline]
18fn join_with_max_length<T: ExactSizeIterator<Item = String>>(
19 strings: T,
20 sep: &str,
21 max_len: usize,
22) -> impl Display {
23 let last_index = strings.len() - 1;
24 let mut string = String::with_capacity(max_len);
25 for (i, item) in strings.enumerate() {
26 let current_sep = if i == 0 { "" } else { sep };
27 let min_next_len = if i == last_index { 0 } else { sep.len() + 3 };
28 if string.char_count()
29 + current_sep.len()
30 + item.char_count()
31 + min_next_len
32 > max_len
33 {
34 string.extend([current_sep, "..."]);
35 break;
36 }
37 string.extend([current_sep, &item]);
38 }
39 debug_assert!(string.char_count() <= max_len);
40 string
41}
42
43cfg_if! {
44 if #[cfg(feature = "pyo3")] {
45 #[pyclass]
46 pub struct HangmanResult {
47 #[pyo3(get)]
48 pub input: String,
49 #[pyo3(get)]
50 pub matching_words_count: u32,
51 #[pyo3(get)]
52 pub invalid: Vec<char>,
53 #[pyo3(get, name = "words")]
54 pub possible_words: Vec<&'static str>,
55 #[pyo3(get)]
56 pub language: Language,
57 #[pyo3(get)]
58 pub letter_frequency: Vec<(char, u32)>,
59 }
60
61 #[pymethods]
62 impl HangmanResult {
63 fn __repr__(&self) -> String {
64 let id = self as *const Self;
65 let count = self.matching_words_count;
66 let lang = self.language.name();
67 let pattern = &self.input;
68 let invalid = &self.invalid;
69
70 if let Some(word) = (count == 1).then_some(()).and_then(|()| self.possible_words.first()) {
71 format!("<HangmanResult lang={lang} pattern={pattern} invalid={invalid:?} count={count} word={word} at {id:?}>")
72 } else if count == 1 {
73 let letters: Box<[char]> = self.letter_frequency.iter().map(|(ch, _)| *ch).collect();
74 format!("<HangmanResult lang={lang} pattern={pattern} invalid={invalid:?} count={count} letters={letters:?} at {id:?}>")
75 } else if let Some(mcl) = self.letter_frequency.first().map(|(ch, _)| ch) {
76 format!("<HangmanResult lang={lang} pattern={pattern} invalid={invalid:?} count={count} guess={mcl} at {id:?}>")
77 } else {
78 format!("<HangmanResult lang={lang} pattern={pattern} invalid={invalid:?} count={count} at {id:?}>")
79 }
80 }
81 }
82 } else {
83 pub struct HangmanResult {
84 pub input: String,
85 pub invalid: Vec<char>,
86 pub matching_words_count: u32,
87 pub possible_words: Vec<&'static str>,
88 pub language: Language,
89 pub letter_frequency: Vec<(char, u32)>,
90 }
91 }
92}
93
94impl std::fmt::Display for HangmanResult {
95 fn fmt(&self, file: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 let max_line_length: usize = file.width().unwrap_or(80);
97 let invalid: String = self.invalid.iter().collect();
98 write!(
99 file,
100 "Found {} words (input: {}, invalid: {})",
101 self.matching_words_count, self.input, invalid,
102 )?;
103 if self.possible_words.is_empty() {
104 return Ok(());
105 }
106 writeln!(file)?;
107 write!(
108 file,
109 " words: {}",
110 join_with_max_length(
111 self.possible_words.iter().map(|word| String::from(*word)),
112 ", ",
113 max_line_length - " words: ".len(),
114 )
115 )?;
116
117 if !self.letter_frequency.is_empty() {
118 writeln!(file)?;
119 write!(
120 file,
121 " letters: {}",
122 join_with_max_length(
123 self.letter_frequency
124 .iter()
125 .map(|(ch, f)| format!("{ch}: {f}")),
126 ", ",
127 max_line_length - " letters: ".len(),
128 )
129 )?;
130 }
131 Ok(())
132 }
133}
134
135#[cfg(feature = "wasm-bindgen")]
136#[wasm_bindgen(getter_with_clone)]
137pub struct WasmHangmanResult {
138 #[wasm_bindgen(readonly)]
139 pub input: JsString,
140 #[wasm_bindgen(readonly)]
141 pub invalid: JsString,
142 #[wasm_bindgen(readonly)]
143 pub matching_words_count: u32,
144 #[wasm_bindgen(readonly)]
145 pub possible_words: Vec<JsString>,
146 #[wasm_bindgen(readonly)]
147 pub letter_frequency: JsString,
148}