hangman_solver_lib/language/
mod.rs

1// SPDX-License-Identifier: EUPL-1.2
2
3pub mod word_sequence;
4
5use std::num::NonZeroUsize;
6
7#[cfg(feature = "pyo3")]
8use std::ops::Div;
9
10#[cfg(feature = "pyo3")]
11use pyo3::create_exception;
12#[cfg(feature = "pyo3")]
13use pyo3::exceptions::PyValueError;
14#[cfg(feature = "pyo3")]
15use pyo3::prelude::*;
16
17pub use word_sequence::WordSequence;
18
19#[allow(unsafe_code)]
20#[cfg_attr(feature = "pyo3", pyclass)]
21pub struct StringChunkIter {
22    padded_word_byte_count: NonZeroUsize,
23    is_ascii: bool,
24    index: usize,
25    string: &'static str,
26}
27
28impl Iterator for StringChunkIter {
29    type Item = &'static str;
30
31    #[inline]
32    fn next(&mut self) -> Option<Self::Item> {
33        let end_index =
34            self.index.checked_add(self.padded_word_byte_count.get())?;
35        debug_assert_ne!(self.index, end_index);
36
37        let result = self.string.get(self.index..end_index)?;
38
39        let result = if self.is_ascii {
40            result
41        } else {
42            result.trim_start_matches('\0')
43        };
44
45        debug_assert!(end_index <= self.string.len());
46        self.index = end_index;
47        debug_assert!(!result.contains('\0'));
48        Some(result)
49    }
50}
51
52#[cfg(feature = "pyo3")]
53#[pymethods]
54impl StringChunkIter {
55    #[must_use]
56    const fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
57        slf
58    }
59
60    #[must_use]
61    fn __next__(&mut self) -> Option<&'static str> {
62        self.next()
63    }
64
65    #[must_use]
66    pub fn __len__(&self) -> usize {
67        self.string
68            .len()
69            .checked_sub(self.index)
70            .map_or(0, |rest| rest.div(self.padded_word_byte_count))
71    }
72}
73
74include!(concat!(env!("OUT_DIR"), "/language.rs"));
75
76#[cfg(feature = "pyo3")]
77create_exception!(hangman_solver, UnknownLanguageError, PyValueError);