1use std::io;
4use std::path::PathBuf;
5use thiserror::Error;
6
7pub const FRONT_MATTER_ERROR_MAX_LINES: usize = 20;
10
11#[derive(Debug, Error, PartialEq)]
13pub enum InputStreamError {
14 #[error(
17 "The HTML input stream starts with a doctype other than\n\
18 \"<!DOCTYPE html>\":\n\
19 {html}"
20 )]
21 NonHtmlDoctype { html: String },
22}
23
24#[derive(Debug, Error)]
26pub enum FileError {
27 #[error(
29 "Can not find unused filename in directory:\n\
30 \t{directory:?}\n\
31 (only `COPY_COUNTER_MAX` copies are allowed)."
32 )]
33 NoFreeFileName { directory: PathBuf },
34
35 #[error(transparent)]
36 Io(#[from] std::io::Error),
37
38 #[error(transparent)]
39 Serialize(#[from] toml::ser::Error),
40
41 #[error(transparent)]
42 Deserialize(#[from] toml::de::Error),
43}
44
45#[derive(Debug, Error, Clone, PartialEq)]
47pub enum LibCfgError {
48 #[error("Input data root must be a `Value::Table`")]
52 CfgValInputIsNotTable,
53
54 #[error(
56 "Configuration file error in section:\n\
57 \t[[scheme]]\n\
58 \tscheme_default = \"{scheme_name}\"\n\
59 No scheme found. Available configured schemes:\n\
60 {schemes}
61 "
62 )]
63 SchemeNotFound {
64 scheme_name: String,
65 schemes: String,
66 },
67
68 #[error(
70 "Configuration file error in [base_scheme] or in section:\n\
71 \t[[scheme]]\n\
72 \tname = \"{scheme_name}\"\n\
73 \t[scheme.tmpl]\n\
74 \tfilter.get_lang.relative_distance_min={dist}\n\
75 must be between 0.0 and 0.99."
76 )]
77 MinimumRelativeDistanceInvalid { scheme_name: String, dist: f64 },
78
79 #[error(
81 "Configuration file error in [base_scheme] or in section:\n\
82 \t[[scheme]]\n\
83 \tname = \"{scheme_name}\"
84 \t[scheme.filename]\n\
85 \tsort_tag.extra_separator=\"{extra_separator}\"\n\
86 must not be one of `sort_tag_extra_chars=\"{sort_tag_extra_chars}\"`,\n\
87 `0..9`, `a..z` or `{dot_file_marker}`."
88 )]
89 SortTagExtraSeparator {
90 scheme_name: String,
91 dot_file_marker: char,
92 sort_tag_extra_chars: String,
93 extra_separator: String,
94 },
95
96 #[error(
99 "Configuration file error in [base_scheme] or in section:\n\
100 \t[[scheme]]\n\
101 \tname = \"{scheme_name}\"
102 \t[scheme.filename]\n\
103 \t`extension_default=\"{extension_default}\"\n\
104 must not be one of:`\n\
105 \t{extensions}."
106 )]
107 ExtensionDefault {
108 scheme_name: String,
109 extension_default: String,
110 extensions: String,
111 },
112
113 #[error(
115 "Configuration file error in [base_scheme] or in section:\n\
116 \t[[scheme]]\n\
117 \tname = \"{scheme_name}\"
118 \t[scheme.filename]\n\
119 All characters in `sort_tag.separator=\"{separator}\"\n\
120 must be in the set `sort_tag.extra_chars=\"{chars}\"`,\n\
121 or in `0..9`, `a..z``\n\
122 must NOT start with `{dot_file_marker}`."
123 )]
124 SortTagSeparator {
125 scheme_name: String,
126 dot_file_marker: char,
127 chars: String,
128 separator: String,
129 },
130
131 #[error(
133 "Configuration file error in [base_scheme] or in section:\n\
134 \t[[scheme]]\n\
135 \tname = \"{scheme_name}\"
136 \t[scheme.filename]\n\
137 `copy_counter.extra_separator=\"{extra_separator}\"`\n\
138 must be one of: \"{chars}\""
139 )]
140 CopyCounterExtraSeparator {
141 scheme_name: String,
142 chars: String,
143 extra_separator: String,
144 },
145
146 #[error("choose one of: `IsDefined`, `IsString`, `IsNumber`, `IsStringOrNumber`, `IsBool`, `IsValidSortTag`")]
148 ParseAssertPrecondition,
149
150 #[error("choose one of: `off`, `short` or `long`")]
152 ParseLocalLinkKind,
153
154 #[error(
158 "The ISO 639-1 language subtag `{language_code}`\n\
159 in the configuration file variable\n\
160 `tmpl.filter.get_lang.language_candidates` or in the\n\
161 environment variable `TPNOTE_LANG_DETECTION` is not\n\
162 supported. All listed codes must be part of the set:\n\
163 {all_langs}."
164 )]
165 ParseLanguageCode {
166 language_code: String,
167 all_langs: String,
168 },
169
170 #[error(
175 "Not enough languages to choose from.\n\
176 The list of ISO 639-1 language subtags\n\
177 currently contains only one item: `{language_code}`.\n\
178 Add one more language to the configuration \n\
179 file variable `tmpl.filter.get_lang` or to the\n\
180 environment variable `TPNOTE_LANG_DETECTION`\n\
181 to prevent this error from occurring."
182 )]
183 NotEnoughLanguageCodes { language_code: String },
184
185 #[error(
187 "Configuration file error in section `[tmp_html]` in line:\n\
188 \t{var} = \"{value}\"\n\
189 The theme must be one of the following set:\n\
190 {available}"
191 )]
192 HighlightingThemeName {
193 var: String,
194 value: String,
195 available: String,
196 },
197
198 #[error(transparent)]
199 Deserialize(#[from] toml::de::Error),
200}
201
202#[derive(Debug, Error)]
203pub enum NoteError {
205 #[error("<NONE FOUND: {path}...>")]
207 CanNotExpandShorthandLink { path: String },
208
209 #[error("Can not prepend header. File has one already: \n{existing_header}")]
211 CannotPrependHeader { existing_header: String },
212
213 #[error(transparent)]
214 File(#[from] FileError),
215
216 #[error(
218 "Invalid header variable value: no scheme `{scheme_val}` found.\n\
219 \t---\n\
220 \t{scheme_key}: {scheme_val}\n\
221 \t---\n\n\
222 Available schemes in configuration file:\n\
223 {schemes}
224 "
225 )]
226 SchemeNotFound {
227 scheme_val: String,
228 scheme_key: String,
229 schemes: String,
230 },
231
232 #[error(
234 "The `sort_tag` header variable contains invalid\n\
235 character(s):\n\n\
236 \t---\n\
237 \tsort_tag: {sort_tag}\n\
238 \t---\n\n\
239 Only the characters: \"{sort_tag_extra_chars}\", `0..9`\n\
240 and `a..z` (maximum {filename_sort_tag_letters_in_succession_max} in \
241 succession) are allowed."
242 )]
243 FrontMatterFieldIsInvalidSortTag {
244 sort_tag: String,
245 sort_tag_extra_chars: String,
246 filename_sort_tag_letters_in_succession_max: u8,
247 },
248
249 #[error(
251 "This `sort_tag` header variable is a sequential sort-tag:\n\
252 \t---\n\
253 \tsort_tag: {sort_tag}\n\
254 \t---\n\n\
255 A file with this sort-tag exists already on disk:\n\n\
256 \t`{existing_file}`\n\n\
257 For sequential sort-tags no duplicates are allowed.\n\
258 Please choose another sort-tag.
259 "
260 )]
261 FrontMatterFieldIsDuplicateSortTag {
262 sort_tag: String,
263 existing_file: String,
264 },
265
266 #[error(
268 "The type of the front matter field `{field_name}:`\n\
269 must not be a compound type. Use a simple type, \n\
270 i.e. `String`, `Number` or `Bool` instead. Example:\n\
271 \n\
272 \t~~~~~~~~~~~~~~\n\
273 \t---\n\
274 \t{field_name}: My simple type\n\
275 \t---\n\
276 \tsome text\n\
277 \t~~~~~~~~~~~~~~"
278 )]
279 FrontMatterFieldIsCompound { field_name: String },
280
281 #[error(
283 "The (sub)type of the front matter field `{field_name}:`\n\
284 must be a non empty `String`. Example:\n\
285 \n\
286 \t~~~~~~~~~~~~~~\n\
287 \t---\n\
288 \t{field_name}: My string\n\
289 \t---\n\
290 \tsome text\n\
291 \t~~~~~~~~~~~~~~"
292 )]
293 FrontMatterFieldIsEmptyString { field_name: String },
294
295 #[error(
297 "The (sub)type of the front matter field `{field_name}:`\n\
298 must be `Bool`. Example:\n\
299 \n\
300 \t~~~~~~~~~~~~~~\n\
301 \t---\n\
302 \t{field_name}: false\n\
303 \t---\n\
304 \tsome text\n\
305 \t~~~~~~~~~~~~~~\n\
306 \n\
307 Hint: try to remove possible quotes."
308 )]
309 FrontMatterFieldIsNotBool { field_name: String },
310
311 #[error(
313 "The (sub)type of the front matter field `{field_name}:`\n\
314 must be `Number`. Example:\n\
315 \n\
316 \t~~~~~~~~~~~~~~\n\
317 \t---\n\
318 \t{field_name}: 142\n\
319 \t---\n\
320 \tsome text\n\
321 \t~~~~~~~~~~~~~~\n\
322 \n\
323 Hint: try to remove possible quotes."
324 )]
325 FrontMatterFieldIsNotNumber { field_name: String },
326
327 #[error(
329 "The (sub)type of the front matter field `{field_name}:`\n\
330 must be `String`. Example:\n\
331 \n\
332 \t~~~~~~~~~~~~~~\n\
333 \t---\n\
334 \t{field_name}: My string\n\
335 \t---\n\
336 \tsome text\n\
337 \t~~~~~~~~~~~~~~\n\
338 \n\
339 Hint: try to enclose with quotes."
340 )]
341 FrontMatterFieldIsNotString { field_name: String },
342
343 #[error(
345 "The file extension:\n\
346 \t---\n\
347 \tfile_ext: {extension}\n\
348 \t---\n\
349 is not registered as Tp-Note file in\n\
350 your configuration file:\n\
351 \t{extensions}\n\
352 \n\
353 Choose one of the listed above or add more extensions to the\n\
354 `filename.extensions` variable in your configuration file."
355 )]
356 FrontMatterFieldIsNotTpnoteExtension {
357 extension: String,
358 extensions: String,
359 },
360
361 #[error(
363 "The document is missing a `{field_name}:`\n\
364 field in its front matter:\n\
365 \n\
366 \t~~~~~~~~~~~~~~\n\
367 \t---\n\
368 \t{field_name}: \"My note\"\n\
369 \t---\n\
370 \tsome text\n\
371 \t~~~~~~~~~~~~~~\n\
372 \n\
373 Please correct the front matter if this is\n\
374 supposed to be a Tp-Note file. Ignore otherwise."
375 )]
376 FrontMatterFieldMissing { field_name: String },
377
378 #[error(
380 "The document (or template) has no front matter\n\
381 section. Is one `---` missing?\n\n\
382 \t~~~~~~~~~~~~~~\n\
383 \t---\n\
384 \t{compulsory_field}: My note\n\
385 \t---\n\
386 \tsome text\n\
387 \t~~~~~~~~~~~~~~\n\
388 \n\
389 Please correct the front matter if this is\n\
390 supposed to be a Tp-Note file. Ignore otherwise."
391 )]
392 FrontMatterMissing { compulsory_field: String },
393
394 #[error(
396 "Can not parse front matter:\n\
397 \n\
398 {front_matter}\
399 \n\
400 {source_error}"
401 )]
402 InvalidFrontMatterYaml {
403 front_matter: String,
404 source_error: serde_yaml::Error,
405 },
406
407 #[error(
409 "Invalid YAML field(s) in the {tmpl_var} input\n\
410 stream data found:\n\
411 {source_str}"
412 )]
413 InvalidInputYaml {
414 tmpl_var: String,
415 source_str: String,
416 },
417
418 #[error(
420 "Invalid HTML in the input stream data found:\n\
421 {source_str}"
422 )]
423 InvalidHtml { source_str: String },
424
425 #[error(
427 "Filter `html_to_markup` is disabled for this \n\
428 `extension_default` in table `scheme.filename.extensions.1`."
429 )]
430 HtmlToMarkupDisabled,
431
432 #[error("<INVALID: {path}>")]
434 InvalidLocalPath { path: String },
435
436 #[error(transparent)]
437 Io(#[from] std::io::Error),
438
439 #[error(transparent)]
440 ParseLanguageCode(#[from] LibCfgError),
441
442 #[error("Can not read file:\n\t {path:?}\n{source}")]
444 Read { path: PathBuf, source: io::Error },
445
446 #[error("Can not parse reStructuredText input:\n{msg}")]
448 #[cfg(feature = "renderer")]
449 RstParse { msg: String },
450
451 #[error(
453 "Tera error:\n\
454 {source}"
455 )]
456 Tera {
457 #[from]
458 source: tera::Error,
459 },
460
461 #[error(
463 "Tera template error in configuration file\n\
464 variable \"{template_str}\":\n {source_str}"
465 )]
466 TeraTemplate {
467 source_str: String,
468 template_str: String,
469 },
470
471 #[error(transparent)]
472 Utf8Conversion {
473 #[from]
474 source: core::str::Utf8Error,
475 },
476}
477
478#[macro_export]
480macro_rules! note_error_tera_template {
481 ($e:ident, $t:expr) => {
482 NoteError::TeraTemplate {
483 source_str: std::error::Error::source(&$e)
484 .unwrap_or(&tera::Error::msg(""))
485 .to_string()
486 .trim_end_matches("in context while rendering '__tera_one_off'")
488 .to_string(),
489 template_str: $t,
490 }
491 };
492}