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 section:\n\
71 \t[[scheme]]\n\
72 \tname = \"{scheme_name}\"
73 \t[scheme.filename]\n\
74 \tsort_tag.extra_separator=\"{extra_separator}\"\n\
75 must not be one of `sort_tag_extra_chars=\"{sort_tag_extra_chars}\"`,\n\
76 `0..9`, `a..z` or `{dot_file_marker}`."
77 )]
78 SortTagExtraSeparator {
79 scheme_name: String,
80 dot_file_marker: char,
81 sort_tag_extra_chars: String,
82 extra_separator: String,
83 },
84
85 #[error(
88 "Configuration file error in section:\n\
89 \t[[scheme]]\n\
90 \tname = \"{scheme_name}\"
91 \t[scheme.filename]\n\
92 \t`extension_default=\"{extension_default}\"\n\
93 must not be one of:`\n\
94 \t{extensions}."
95 )]
96 ExtensionDefault {
97 scheme_name: String,
98 extension_default: String,
99 extensions: String,
100 },
101
102 #[error(
104 "Configuration file error in section:\n\
105 \t[[scheme]]\n\
106 \tname = \"{scheme_name}\"
107 \t[scheme.filename]\n\
108 All characters in `sort_tag.separator=\"{separator}\"\n\
109 must be in the set `sort_tag.extra_chars=\"{chars}\"`,\n\
110 or in `0..9`, `a..z``\n\
111 must NOT start with `{dot_file_marker}`."
112 )]
113 SortTagSeparator {
114 scheme_name: String,
115 dot_file_marker: char,
116 chars: String,
117 separator: String,
118 },
119
120 #[error(
122 "Configuration file error in section:\n\
123 \t[[scheme]]\n\
124 \tname = \"{scheme_name}\"
125 \t[scheme.filename]\n\
126 `copy_counter.extra_separator=\"{extra_separator}\"`\n\
127 must be one of: \"{chars}\""
128 )]
129 CopyCounterExtraSeparator {
130 scheme_name: String,
131 chars: String,
132 extra_separator: String,
133 },
134
135 #[error("choose one of: `IsDefined`, `IsString`, `IsNumber`, `IsStringOrNumber`, `IsBool`, `IsValidSortTag`")]
137 ParseAssertPrecondition,
138
139 #[error("choose one of: `off`, `short` or `long`")]
141 ParseLocalLinkKind,
142
143 #[error(
147 "The ISO 639-1 language subtag `{language_code}`\n\
148 in the configuration file variable\n\
149 `tmpl.filter.get_lang` or in the environment\n\
150 variable `TPNOTE_LANG_DETECTION` is not supported.\n\
151 All listed codes must be part of the set:\n\
152 {all_langs}."
153 )]
154 ParseLanguageCode {
155 language_code: String,
156 all_langs: String,
157 },
158
159 #[error(
163 "Not enough languages to choose from.\n\
164 The list of ISO 639-1 language subtags\n\
165 currently contains only one item: `{language_code}`.\n\
166 Add one more language to the configuration \n\
167 file variable `tmpl.filter.get_lang` or to the\n\
168 environment variable `TPNOTE_LANG_DETECTION`\n\
169 to prevent this error from occurring."
170 )]
171 NotEnoughLanguageCodes { language_code: String },
172
173 #[error(
175 "Configuration file error in section `[tmp_html]` in line:\n\
176 \t{var} = \"{value}\"\n\
177 The theme must be one of the following set:\n\
178 {available}"
179 )]
180 HighlightingThemeName {
181 var: String,
182 value: String,
183 available: String,
184 },
185
186 #[error(transparent)]
187 Deserialize(#[from] toml::de::Error),
188}
189
190#[derive(Debug, Error)]
191pub enum NoteError {
193 #[error("<NONE FOUND: {path}...>")]
195 CanNotExpandShorthandLink { path: String },
196
197 #[error("Can not prepend header. File has one already: \n{existing_header}")]
199 CannotPrependHeader { existing_header: String },
200
201 #[error(transparent)]
202 File(#[from] FileError),
203
204 #[error(
206 "Invalid header variable value: no scheme `{scheme_val}` found.\n\
207 \t---\n\
208 \t{scheme_key}: {scheme_val}\n\
209 \t---\n\n\
210 Available schemes in configuration file:\n\
211 {schemes}
212 "
213 )]
214 SchemeNotFound {
215 scheme_val: String,
216 scheme_key: String,
217 schemes: String,
218 },
219
220 #[error(
222 "The `sort_tag` header variable contains invalid\n\
223 character(s):\n\n\
224 \t---\n\
225 \tsort_tag: {sort_tag}\n\
226 \t---\n\n\
227 Only the characters: \"{sort_tag_extra_chars}\", `0..9`\n\
228 and `a..z` (maximum {filename_sort_tag_letters_in_succession_max} in \
229 succession) are allowed."
230 )]
231 FrontMatterFieldIsInvalidSortTag {
232 sort_tag: String,
233 sort_tag_extra_chars: String,
234 filename_sort_tag_letters_in_succession_max: u8,
235 },
236
237 #[error(
239 "This `sort_tag` header variable is a sequential sort-tag:\n\
240 \t---\n\
241 \tsort_tag: {sort_tag}\n\
242 \t---\n\n\
243 A file with this sort-tag exists already on disk:\n\n\
244 \t`{existing_file}`\n\n\
245 For sequential sort-tags no duplicates are allowed.\n\
246 Please choose another sort-tag.
247 "
248 )]
249 FrontMatterFieldIsDuplicateSortTag {
250 sort_tag: String,
251 existing_file: String,
252 },
253
254 #[error(
256 "The type of the front matter field `{field_name}:`\n\
257 must not be a compound type. Use a simple type, \n\
258 i.e. `String`, `Number` or `Bool` instead. Example:\n\
259 \n\
260 \t~~~~~~~~~~~~~~\n\
261 \t---\n\
262 \t{field_name}: My simple type\n\
263 \t---\n\
264 \tsome text\n\
265 \t~~~~~~~~~~~~~~"
266 )]
267 FrontMatterFieldIsCompound { field_name: String },
268
269 #[error(
271 "The (sub)type of the front matter field `{field_name}:`\n\
272 must be a non empty `String`. Example:\n\
273 \n\
274 \t~~~~~~~~~~~~~~\n\
275 \t---\n\
276 \t{field_name}: My string\n\
277 \t---\n\
278 \tsome text\n\
279 \t~~~~~~~~~~~~~~"
280 )]
281 FrontMatterFieldIsEmptyString { field_name: String },
282
283 #[error(
285 "The (sub)type of the front matter field `{field_name}:`\n\
286 must be `Bool`. Example:\n\
287 \n\
288 \t~~~~~~~~~~~~~~\n\
289 \t---\n\
290 \t{field_name}: false\n\
291 \t---\n\
292 \tsome text\n\
293 \t~~~~~~~~~~~~~~\n\
294 \n\
295 Hint: try to remove possible quotes."
296 )]
297 FrontMatterFieldIsNotBool { field_name: String },
298
299 #[error(
301 "The (sub)type of the front matter field `{field_name}:`\n\
302 must be `Number`. Example:\n\
303 \n\
304 \t~~~~~~~~~~~~~~\n\
305 \t---\n\
306 \t{field_name}: 142\n\
307 \t---\n\
308 \tsome text\n\
309 \t~~~~~~~~~~~~~~\n\
310 \n\
311 Hint: try to remove possible quotes."
312 )]
313 FrontMatterFieldIsNotNumber { field_name: String },
314
315 #[error(
317 "The (sub)type of the front matter field `{field_name}:`\n\
318 must be `String`. Example:\n\
319 \n\
320 \t~~~~~~~~~~~~~~\n\
321 \t---\n\
322 \t{field_name}: My string\n\
323 \t---\n\
324 \tsome text\n\
325 \t~~~~~~~~~~~~~~\n\
326 \n\
327 Hint: try to enclose with quotes."
328 )]
329 FrontMatterFieldIsNotString { field_name: String },
330
331 #[error(
333 "The file extension:\n\
334 \t---\n\
335 \tfile_ext: {extension}\n\
336 \t---\n\
337 is not registered as Tp-Note file in\n\
338 your configuration file:\n\
339 \t{extensions}\n\
340 \n\
341 Choose one of the listed above or add more extensions to the\n\
342 `filename.extensions` variable in your configuration file."
343 )]
344 FrontMatterFieldIsNotTpnoteExtension {
345 extension: String,
346 extensions: String,
347 },
348
349 #[error(
351 "The document is missing a `{field_name}:`\n\
352 field in its front matter:\n\
353 \n\
354 \t~~~~~~~~~~~~~~\n\
355 \t---\n\
356 \t{field_name}: \"My note\"\n\
357 \t---\n\
358 \tsome text\n\
359 \t~~~~~~~~~~~~~~\n\
360 \n\
361 Please correct the front matter if this is\n\
362 supposed to be a Tp-Note file. Ignore otherwise."
363 )]
364 FrontMatterFieldMissing { field_name: String },
365
366 #[error(
368 "The document (or template) has no front matter\n\
369 section. Is one `---` missing?\n\n\
370 \t~~~~~~~~~~~~~~\n\
371 \t---\n\
372 \t{compulsory_field}: My note\n\
373 \t---\n\
374 \tsome text\n\
375 \t~~~~~~~~~~~~~~\n\
376 \n\
377 Please correct the front matter if this is\n\
378 supposed to be a Tp-Note file. Ignore otherwise."
379 )]
380 FrontMatterMissing { compulsory_field: String },
381
382 #[error(
384 "Can not parse front matter:\n\
385 \n\
386 {front_matter}\
387 \n\
388 {source_error}"
389 )]
390 InvalidFrontMatterYaml {
391 front_matter: String,
392 source_error: serde_yaml::Error,
393 },
394
395 #[error(
397 "Invalid YAML field(s) in the {tmpl_var} input\n\
398 stream data found:\n\
399 {source_str}"
400 )]
401 InvalidInputYaml {
402 tmpl_var: String,
403 source_str: String,
404 },
405
406 #[error(
408 "Invalid HTML in the input stream data found:\n\
409 {source_str}"
410 )]
411 InvalidHtml { source_str: String },
412
413 #[error(
415 "Filter `html_to_markup` is disabled for this \n\
416 `extension_default` in table `scheme.filename.extensions.1`."
417 )]
418 HtmlToMarkupDisabled,
419
420 #[error("<INVALID: {path}>")]
422 InvalidLocalPath { path: String },
423
424 #[error(transparent)]
425 Io(#[from] std::io::Error),
426
427 #[error(transparent)]
428 ParseLanguageCode(#[from] LibCfgError),
429
430 #[error("Can not read file:\n\t {path:?}\n{source}")]
432 Read { path: PathBuf, source: io::Error },
433
434 #[error("Can not parse reStructuredText input:\n{msg}")]
436 #[cfg(feature = "renderer")]
437 RstParse { msg: String },
438
439 #[error(
441 "Tera error:\n\
442 {source}"
443 )]
444 Tera {
445 #[from]
446 source: tera::Error,
447 },
448
449 #[error(
451 "Tera template error in configuration file\n\
452 variable \"{template_str}\":\n {source_str}"
453 )]
454 TeraTemplate {
455 source_str: String,
456 template_str: String,
457 },
458
459 #[error(transparent)]
460 Utf8Conversion {
461 #[from]
462 source: core::str::Utf8Error,
463 },
464}
465
466#[macro_export]
468macro_rules! note_error_tera_template {
469 ($e:ident, $t:expr) => {
470 NoteError::TeraTemplate {
471 source_str: std::error::Error::source(&$e)
472 .unwrap_or(&tera::Error::msg(""))
473 .to_string()
474 .trim_end_matches("in context while rendering '__tera_one_off'")
476 .to_string(),
477 template_str: $t,
478 }
479 };
480}