1use std::path::PathBuf;
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5#[allow(dead_code)]
6pub enum BuildError {
7 #[error("IO error: {0}")]
8 Io(#[from] std::io::Error),
9
10 #[error("JSON serialization error: {0}")]
11 Json(#[from] serde_json::Error),
12
13 #[error("YAML serialization error: {0}")]
14 Yaml(#[from] serde_yaml::Error),
15
16 #[error("Template rendering error: {0}")]
17 Template(String),
18
19 #[error("File parsing error: {file}: {message}")]
20 Parse { file: String, message: String },
21
22 #[error("Cache error: {0}")]
23 Cache(String),
24
25 #[error("Configuration error: {0}")]
26 Config(String),
27
28 #[error("Thread pool error: {0}")]
29 ThreadPool(#[from] rayon::ThreadPoolBuildError),
30
31 #[error("File not found: {0}")]
32 FileNotFound(String),
33
34 #[error("Invalid document format: {0}")]
35 InvalidFormat(String),
36
37 #[error("Cross-reference error: {reference} not found")]
38 CrossReference { reference: String },
39
40 #[error("Template not found: {0}")]
41 TemplateNotFound(String),
42
43 #[error("Syntax highlighting error: {0}")]
44 SyntaxHighlight(String),
45
46 #[error("Validation error: {0}")]
47 ValidationError(String),
48}
49
50#[derive(Debug, Clone)]
51pub struct BuildWarning {
52 pub file: PathBuf,
53 pub line: Option<usize>,
54 pub message: String,
55 #[allow(dead_code)]
56 pub warning_type: WarningType,
57 pub category: Option<String>,
66}
67
68#[derive(Debug, Clone)]
69pub struct BuildErrorReport {
70 pub file: PathBuf,
71 pub line: Option<usize>,
72 pub message: String,
73 #[allow(dead_code)]
74 pub error_type: ErrorType,
75}
76
77#[derive(Debug, Clone)]
78#[allow(dead_code)]
79pub enum WarningType {
80 MissingToctreeRef,
81 OrphanedDocument,
82 BrokenCrossReference,
83 MissingFile,
84 UnusedLabel,
85 DuplicateLabel,
86 EmptyToctree,
87 Other,
88}
89
90#[derive(Debug, Clone)]
91#[allow(dead_code)]
92pub enum ErrorType {
93 ParseError,
94 FileNotFound,
95 TemplateError,
96 SyntaxError,
97 Other,
98}
99
100impl BuildWarning {
101 pub fn new(
102 file: PathBuf,
103 line: Option<usize>,
104 message: String,
105 warning_type: WarningType,
106 ) -> Self {
107 Self {
108 file,
109 line,
110 message,
111 warning_type,
112 category: None,
113 }
114 }
115
116 #[must_use]
118 pub fn with_category(mut self, category: Option<String>) -> Self {
119 self.category = category;
120 self
121 }
122
123 pub fn render(&self) -> String {
135 let category = match &self.category {
136 Some(category) => format!(" [{category}]"),
137 None => String::new(),
138 };
139 if self.file.as_os_str().is_empty() {
140 return format!("WARNING: {}{category}", self.message);
141 }
142 let line = match self.line {
143 Some(line) => format!(":{line}"),
144 None => String::new(),
145 };
146 format!(
147 "{}{line}: WARNING: {}{category}",
148 self.file.display(),
149 self.message
150 )
151 }
152
153 #[allow(dead_code)]
154 pub fn broken_cross_reference(file: PathBuf, line: Option<usize>, reference: &str) -> Self {
155 Self::new(
156 file,
157 line,
158 format!("cross-reference target not found: '{}'", reference),
159 WarningType::BrokenCrossReference,
160 )
161 }
162}
163
164impl BuildErrorReport {
165 pub fn new(file: PathBuf, line: Option<usize>, message: String, error_type: ErrorType) -> Self {
166 Self {
167 file,
168 line,
169 message,
170 error_type,
171 }
172 }
173}