stork_lib/index_v3/build/
errors.rs

1use std::{fmt, path::PathBuf};
2use thiserror::Error;
3
4use crate::config::File;
5
6#[derive(Debug, Error, Clone, PartialEq)]
7pub enum WordListGenerationError {
8    #[error("SRT file could not be parsed.")]
9    InvalidSRT,
10
11    #[error("The file `{0}` could not be found.")]
12    FileNotFound(PathBuf),
13
14    #[error("Could not determine the file's filetype. Please give this file a file extension Stork knows about, or disambiguate the file's filetype within your config.")]
15    CannotDetermineFiletype,
16
17    #[error("The selector `{0}` is not present in the HTML document.")]
18    SelectorNotPresent(String),
19
20    #[error("The web page could not be fetched")]
21    WebPageNotFetched,
22
23    #[error("When fetched, the web page returned a {0} status code.")]
24    WebPageErrorfulStatusCode(u16),
25
26    #[error("Content-Type is not present or invalid")]
27    UnknownContentType,
28
29    #[error("After parsing the document, there were no words found in the word list.")]
30    EmptyWordList,
31
32    #[error("Stork was not built with the `web-scraping` feature enabled.")]
33    FeatureNotAvailable,
34}
35
36fn pluralize_with_count(count: usize, singular: &str, plural: &str) -> String {
37    format!("{count} {}", if count == 1 { singular } else { plural })
38}
39
40#[derive(Debug, Error)]
41pub enum IndexGenerationError {
42    #[error("No files specified in config file")]
43    NoFilesSpecified,
44
45    #[error("All files failed to be indexed.\n{}", DocumentError::display_list(.0))]
46    AllDocumentErrors(Vec<DocumentError>),
47
48    #[error(
49        "{} found while indexing files. If you want to fail silently and still build an index, remove `break_on_file_error` from your config.\n{}", 
50        pluralize_with_count(.0.len(), "error", "errors"),
51        DocumentError::display_list(.0)
52    )]
53    PartialDocumentErrors(Vec<DocumentError>),
54}
55
56impl PartialEq for IndexGenerationError {
57    fn eq(&self, other: &Self) -> bool {
58        match (self, other) {
59            (Self::PartialDocumentErrors(_), Self::PartialDocumentErrors(_))
60            | (Self::AllDocumentErrors(_), Self::AllDocumentErrors(_)) => true,
61            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
62        }
63    }
64}
65
66/**
67 * Associates a `WordListGenerationError` with a `File`.
68 */
69#[derive(Debug, Clone, PartialEq)]
70pub struct DocumentError {
71    pub file: File,
72    pub word_list_generation_error: WordListGenerationError,
73}
74
75impl std::fmt::Display for DocumentError {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        write!(
78            f,
79            "In file `{}`: {}",
80            self.file, self.word_list_generation_error,
81        )
82    }
83}
84
85impl DocumentError {
86    #[must_use]
87    pub fn display_list(vec: &[DocumentError]) -> String {
88        format!(
89            "Warning: Stork couldn't include {} in the index because of the following errors:\n",
90            pluralize_with_count(vec.len(), "file", "files"),
91        ) + &vec
92            .iter()
93            .map(ToString::to_string)
94            .collect::<Vec<String>>()
95            .join("\n")
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use crate::config::DataSource;
102
103    use super::*;
104    use pretty_assertions::assert_eq;
105
106    #[test]
107    fn test_documenterrors_display() {
108        let computed = DocumentError {
109            file: File {
110                title: "My Test File".to_string(),
111                explicit_source: Some(DataSource::Contents("ignored".to_string())),
112                ..File::default()
113            },
114            word_list_generation_error: WordListGenerationError::FileNotFound(PathBuf::from(
115                "/test",
116            )),
117        }
118        .to_string();
119
120        let expected = "In file `My Test File`: The file `/test` could not be found.";
121        assert_eq!(computed, expected);
122    }
123
124    #[test]
125    fn test_documenterror_list_display() {
126        let computed = DocumentError::display_list(&[
127            DocumentError {
128                file: File {
129                    title: "My Test File".to_string(),
130                    explicit_source: Some(DataSource::Contents("ignored".to_string())),
131                    ..File::default()
132                },
133                word_list_generation_error: WordListGenerationError::FileNotFound(PathBuf::from(
134                    "/test",
135                )),
136            },
137            DocumentError {
138                file: File {
139                    title: "My Test File 2".to_string(),
140                    explicit_source: Some(DataSource::Contents("ignored 2".to_string())),
141                    ..File::default()
142                },
143                word_list_generation_error: WordListGenerationError::FileNotFound(PathBuf::from(
144                    "/test2",
145                )),
146            },
147        ]);
148
149        let expected = "Warning: Stork couldn't include 2 files in the index because of the following errors:\nIn file `My Test File`: The file `/test` could not be found.\nIn file `My Test File 2`: The file `/test2` could not be found.";
150        assert_eq!(computed, expected);
151    }
152}