1use crate::tokens;
2use crate::Dictionary;
3use std::borrow::Cow;
4
5pub fn check_str<'b, 's: 'b>(
6    buffer: &'b str,
7    tokenizer: &'s tokens::Tokenizer,
8    dictionary: &'s dyn Dictionary,
9) -> impl Iterator<Item = Typo<'b>> {
10    tokenizer
11        .parse_str(buffer)
12        .flat_map(move |ident| process_ident(ident, dictionary))
13}
14
15pub fn check_bytes<'b, 's: 'b>(
16    buffer: &'b [u8],
17    tokenizer: &'s tokens::Tokenizer,
18    dictionary: &'s dyn Dictionary,
19) -> impl Iterator<Item = Typo<'b>> {
20    tokenizer
21        .parse_bytes(buffer)
22        .flat_map(move |ident| process_ident(ident, dictionary))
23}
24
25fn process_ident<'i, 's: 'i>(
26    ident: tokens::Identifier<'i>,
27    dictionary: &'s dyn Dictionary,
28) -> impl Iterator<Item = Typo<'i>> {
29    match dictionary.correct_ident(ident) {
30        Some(crate::Status::Valid) => itertools::Either::Left(None.into_iter()),
31        Some(corrections) => {
32            let typo = Typo {
33                byte_offset: ident.offset(),
34                typo: ident.token().into(),
35                corrections,
36            };
37            itertools::Either::Left(Some(typo).into_iter())
38        }
39        None => itertools::Either::Right(
40            ident
41                .split()
42                .filter_map(move |word| process_word(word, dictionary)),
43        ),
44    }
45}
46
47fn process_word<'w, 's: 'w>(
48    word: tokens::Word<'w>,
49    dictionary: &'s dyn Dictionary,
50) -> Option<Typo<'w>> {
51    match dictionary.correct_word(word) {
52        Some(crate::Status::Valid) => None,
53        Some(corrections) => {
54            let typo = Typo {
55                byte_offset: word.offset(),
56                typo: word.token().into(),
57                corrections,
58            };
59            Some(typo)
60        }
61        None => None,
62    }
63}
64
65#[derive(Clone, Debug)]
67pub struct Typo<'m> {
68    pub byte_offset: usize,
69    pub typo: Cow<'m, str>,
70    pub corrections: crate::Status<'m>,
71}
72
73impl Typo<'_> {
74    pub fn into_owned(self) -> Typo<'static> {
75        Typo {
76            byte_offset: self.byte_offset,
77            typo: Cow::Owned(self.typo.into_owned()),
78            corrections: self.corrections.into_owned(),
79        }
80    }
81
82    pub fn borrow(&self) -> Typo<'_> {
83        Typo {
84            byte_offset: self.byte_offset,
85            typo: Cow::Borrowed(self.typo.as_ref()),
86            corrections: self.corrections.borrow(),
87        }
88    }
89
90    pub fn span(&self) -> std::ops::Range<usize> {
91        let start = self.byte_offset;
92        let end = start + self.typo.len();
93        start..end
94    }
95}
96
97impl Default for Typo<'_> {
98    fn default() -> Self {
99        Self {
100            byte_offset: 0,
101            typo: "".into(),
102            corrections: crate::Status::Invalid,
103        }
104    }
105}