Skip to main content

sericom_core/configs/
errors.rs

1//! This module contains custom errors to provide useful feedback to the user
2//! for errors that occur when attempting to parse their `config.toml` file.
3
4use crossterm::style::Stylize;
5use miette::{NamedSource, SourceSpan};
6use std::ops::Range;
7
8/// A wrapper around error types that may arise from attempting to parse a config
9/// file.
10///
11/// Used to allow better, more specific, handling of errors that may arise
12/// from parsing the file.
13///
14/// [`ConfigError::AlreadyInitialized`] should theorhetically never arise;
15/// however, in the situation where [`initialize_config()`][`super::initialize_config()`] were called and
16/// [`CONFIG`][`super::CONFIG`] is already constructed - [`ConfigError::AlreadyInitialized`]
17/// would be the error.
18#[derive(Debug, miette::Diagnostic, thiserror::Error)]
19pub enum ConfigError {
20    #[error(transparent)]
21    IoError(#[from] std::io::Error),
22    #[error(transparent)]
23    #[diagnostic(transparent)]
24    TomlError(#[from] TomlError),
25    #[error(
26        "Config already initialized.\nPlease report the bug to {}", "https://github.com/tkatter/sericom".bold()
27    )]
28    AlreadyInitialized,
29}
30
31/// A wrapper around [`toml::de::Error`] to print custom error messages with [`miette`].
32#[derive(thiserror::Error, miette::Diagnostic, Debug)]
33#[error("{}", "Error reading config file".red())]
34#[diagnostic(
35    code("See valid config options"),
36    url("https://github.com/tkatter/sericom/blob/main/configuration/values.md"),
37    help("{}", self.msg.split_once(',').unwrap_or(("", self.msg.as_str())).1.trim())
38)]
39pub struct TomlError {
40    #[label("{}", self.msg.split_once(',').unwrap_or((self.msg.as_str(), "")).0.trim())]
41    at: SourceSpan,
42    #[source_code]
43    src: NamedSource<String>,
44    msg: String,
45}
46
47impl TomlError {
48    pub(crate) fn new(span: Range<usize>, source: String, message: String) -> Self {
49        let span_len = span.end - span.start;
50        let at: SourceSpan = (span.start, span_len).into();
51        let src = NamedSource::new("config.toml", source);
52        let msg = message;
53        Self { at, src, msg }
54    }
55}