From 6d9f33fb871346d02a4d8add2592b85506c08299 Mon Sep 17 00:00:00 2001 From: Ruben Nijveld <ruben@tweedegolf.com> Date: Thu, 18 Apr 2024 14:29:09 +0200 Subject: [PATCH] Remove thiserror from dependencies and replace with handwritten implementation --- Cargo.lock | 23 ---------- Cargo.toml | 3 +- ntp-proto/Cargo.toml | 1 - ntp-proto/src/nts_record.rs | 74 +++++++++++++++++++++++-------- ntp-proto/src/packet/crypto.rs | 25 ++++++++--- ntp-proto/src/peer.rs | 37 +++++++++++++--- ntp-proto/src/server.rs | 27 ++++++++--- ntpd/Cargo.toml | 1 - ntpd/src/daemon/config/mod.rs | 33 +++++++++++--- ntpd/src/daemon/config/subnet.rs | 31 ++++++++++--- ntpd/src/daemon/spawn/dummy.rs | 12 ++++- ntpd/src/daemon/spawn/nts.rs | 23 ++++++++-- ntpd/src/daemon/spawn/nts_pool.rs | 23 ++++++++-- ntpd/src/daemon/spawn/pool.rs | 12 ++++- ntpd/src/daemon/spawn/standard.rs | 23 ++++++++-- nts-pool-ke/Cargo.toml | 3 +- nts-pool-ke/src/config.rs | 33 +++++++++++--- 17 files changed, 285 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 89cac78b5..53836671f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,6 @@ dependencies = [ "rustls-pemfile", "serde", "serde_test", - "thiserror", "tracing", "zeroize", ] @@ -379,7 +378,6 @@ dependencies = [ "rustls-pemfile", "serde", "serde_json", - "thiserror", "timestamped-socket", "tokio", "tokio-rustls", @@ -397,7 +395,6 @@ dependencies = [ "rustls-native-certs", "rustls-pemfile", "serde", - "thiserror", "tokio", "tokio-rustls", "toml", @@ -715,26 +712,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "thiserror" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread_local" version = "1.1.7" diff --git a/Cargo.toml b/Cargo.toml index 2f4fea0ab..f46c0cea1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,10 +39,9 @@ serde_json = "1.0" serde_test = "1.0.176" rand = "0.8.0" arbitrary = { version = "1.0" } -thiserror = "1.0.10" libc = "0.2.145" tokio = "1.32" -toml = ">=0.5.0,<0.9.0" +toml = { version = ">=0.5.0,<0.9.0", default-features = false, features = ["parse"] } async-trait = "0.1.22" timestamped-socket = "0.2.1" clock-steering = "0.2.0" diff --git a/ntp-proto/Cargo.toml b/ntp-proto/Cargo.toml index 4ae7d0c62..3f6dcdbb8 100644 --- a/ntp-proto/Cargo.toml +++ b/ntp-proto/Cargo.toml @@ -28,7 +28,6 @@ tracing.workspace = true serde.workspace = true arbitrary = { workspace = true, optional = true } rustls.workspace = true -thiserror.workspace = true aead.workspace = true aes-siv.workspace = true zeroize.workspace = true diff --git a/ntp-proto/src/nts_record.rs b/ntp-proto/src/nts_record.rs index 9dd3dc1d0..cb749235d 100644 --- a/ntp-proto/src/nts_record.rs +++ b/ntp-proto/src/nts_record.rs @@ -1,4 +1,5 @@ use std::{ + fmt::Display, io::{Read, Write}, ops::ControlFlow, sync::Arc, @@ -642,38 +643,75 @@ impl NtsRecordDecoder { } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum KeyExchangeError { - #[error("Unrecognized record is marked as critical")] UnrecognizedCriticalRecord, - #[error("Remote: Bad request")] BadRequest, - #[error("Remote: Internal server error")] InternalServerError, - #[error("Remote: Error with unknown code {0}")] UnknownErrorCode(u16), - #[error("The server response is invalid")] BadResponse, - #[error("No continuation protocol supported by both us and server")] NoValidProtocol, - #[error("No encryption algorithm supported by both us and server")] NoValidAlgorithm, - #[error("The length of a fixed key does not match the algorithm used")] InvalidFixedKeyLength, - #[error("Missing cookies")] NoCookies, - #[error("{0}")] - Io(#[from] std::io::Error), - #[error("{0}")] - Tls(#[from] rustls::Error), - #[error("{0}")] + Io(std::io::Error), + Tls(rustls::Error), Certificate(rustls::Error), - #[error("{0}")] - DnsName(#[from] rustls::pki_types::InvalidDnsNameError), - #[error("Incomplete response")] + DnsName(rustls::pki_types::InvalidDnsNameError), IncompleteResponse, } +impl Display for KeyExchangeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::UnrecognizedCriticalRecord => { + write!(f, "Unrecognized record is marked as critical") + } + Self::BadRequest => write!(f, "Remote: Bad request"), + Self::InternalServerError => write!(f, "Remote: Internal server error"), + Self::UnknownErrorCode(e) => write!(f, "Remote: Error with unknown code {e}"), + Self::BadResponse => write!(f, "The server response is invalid"), + Self::NoValidProtocol => write!( + f, + "No continuation protocol supported by both us and server" + ), + Self::NoValidAlgorithm => { + write!(f, "No encryption algorithm supported by both us and server") + } + Self::InvalidFixedKeyLength => write!( + f, + "The length of a fixed key does not match the algorithm used" + ), + Self::NoCookies => write!(f, "Missing cookies"), + Self::Io(e) => write!(f, "{e}"), + Self::Tls(e) => write!(f, "{e}"), + Self::Certificate(e) => write!(f, "{e}"), + Self::DnsName(e) => write!(f, "{e}"), + Self::IncompleteResponse => write!(f, "Incomplete response"), + } + } +} + +impl From<std::io::Error> for KeyExchangeError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + +impl From<rustls::Error> for KeyExchangeError { + fn from(value: rustls::Error) -> Self { + Self::Tls(value) + } +} + +impl From<rustls::pki_types::InvalidDnsNameError> for KeyExchangeError { + fn from(value: rustls::pki_types::InvalidDnsNameError) -> Self { + Self::DnsName(value) + } +} + +impl std::error::Error for KeyExchangeError {} + impl KeyExchangeError { pub(crate) fn from_error_code(error_code: u16) -> Self { match error_code { diff --git a/ntp-proto/src/packet/crypto.rs b/ntp-proto/src/packet/crypto.rs index d0da64171..6eb52f2ff 100644 --- a/ntp-proto/src/packet/crypto.rs +++ b/ntp-proto/src/packet/crypto.rs @@ -1,20 +1,35 @@ +use std::fmt::Display; + use aes_siv::{siv::Aes128Siv, siv::Aes256Siv, Key, KeyInit}; use rand::Rng; -use tracing::error; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::keyset::DecodedServerCookie; use super::extension_fields::ExtensionField; -#[derive(Debug, thiserror::Error)] -#[error("Could not decrypt ciphertext")] +#[derive(Debug)] pub struct DecryptError; -#[derive(Debug, thiserror::Error)] -#[error("Invalid key")] +impl Display for DecryptError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not decrypt ciphertext") + } +} + +impl std::error::Error for DecryptError {} + +#[derive(Debug)] pub struct KeyError; +impl Display for KeyError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Invalid key") + } +} + +impl std::error::Error for KeyError {} + struct Buffer<'a> { buffer: &'a mut [u8], valid: usize, diff --git a/ntp-proto/src/peer.rs b/ntp-proto/src/peer.rs index d0a875bfd..b77b8818f 100644 --- a/ntp-proto/src/peer.rs +++ b/ntp-proto/src/peer.rs @@ -13,6 +13,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use std::{ + fmt::Display, io::Cursor, net::{IpAddr, SocketAddr}, }; @@ -22,12 +23,21 @@ const MAX_STRATUM: u8 = 16; const POLL_WINDOW: std::time::Duration = std::time::Duration::from_secs(5); const STARTUP_TRIES_THRESHOLD: usize = 3; -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum NtsError { - #[error("Ran out of nts cookies")] OutOfCookies, } +impl Display for NtsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::OutOfCookies => write!(f, "Ran out of NTS cookies"), + } + } +} + +impl std::error::Error for NtsError {} + pub struct PeerNtsData { pub(crate) cookies: CookieStash, // Note: we use Box<dyn Cipher> to support the use @@ -323,14 +333,29 @@ pub enum Update { NewMeasurement(PeerSnapshot, Measurement), } -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum PollError { - #[error("{0}")] - Io(#[from] std::io::Error), - #[error("peer unreachable")] + Io(std::io::Error), PeerUnreachable, } +impl Display for PollError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Io(e) => write!(f, "{e}"), + Self::PeerUnreachable => write!(f, "peer unreachable"), + } + } +} + +impl std::error::Error for PollError {} + +impl From<std::io::Error> for PollError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ProtocolVersion { V4, diff --git a/ntp-proto/src/server.rs b/ntp-proto/src/server.rs index dbabeb3cf..349c71497 100644 --- a/ntp-proto/src/server.rs +++ b/ntp-proto/src/server.rs @@ -1,5 +1,6 @@ use std::{ collections::hash_map::RandomState, + fmt::Display, io::Cursor, net::{AddrParseError, IpAddr}, sync::Arc, @@ -7,7 +8,6 @@ use std::{ }; use serde::{de, Deserialize, Deserializer}; -use thiserror::Error; use crate::{ ipfilter::IpFilter, KeySet, NoCipher, NtpClock, NtpPacket, NtpTimestamp, PacketParsingError, @@ -341,16 +341,31 @@ pub struct IpSubnet { pub mask: u8, } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SubnetParseError { - #[error("Invalid subnet syntax")] Subnet, - #[error("{0} in subnet")] - Ip(#[from] AddrParseError), - #[error("Invalid subnet mask")] + Ip(AddrParseError), Mask, } +impl std::error::Error for SubnetParseError {} + +impl Display for SubnetParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Subnet => write!(f, "Invalid subnet syntax"), + Self::Ip(e) => write!(f, "{e} in subnet"), + Self::Mask => write!(f, "Invalid subnet mask"), + } + } +} + +impl From<AddrParseError> for SubnetParseError { + fn from(value: AddrParseError) -> Self { + Self::Ip(value) + } +} + impl std::str::FromStr for IpSubnet { type Err = SubnetParseError; diff --git a/ntpd/Cargo.toml b/ntpd/Cargo.toml index 32352a002..cf1a1cb33 100644 --- a/ntpd/Cargo.toml +++ b/ntpd/Cargo.toml @@ -18,7 +18,6 @@ tokio = { workspace = true, features = ["rt-multi-thread", "io-util", "io-std", tracing.workspace = true tracing-subscriber.workspace = true toml.workspace = true -thiserror.workspace = true rand.workspace = true libc.workspace = true async-trait.workspace = true diff --git a/ntpd/src/daemon/config/mod.rs b/ntpd/src/daemon/config/mod.rs index 4295bdb2b..01a1a682c 100644 --- a/ntpd/src/daemon/config/mod.rs +++ b/ntpd/src/daemon/config/mod.rs @@ -8,13 +8,13 @@ pub use peer::*; use serde::{Deserialize, Deserializer}; pub use server::*; use std::{ + fmt::Display, io::ErrorKind, net::SocketAddr, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, str::FromStr, }; -use thiserror::Error; use timestamped_socket::interface::InterfaceName; use tokio::{fs::read_to_string, io}; use tracing::{info, warn}; @@ -454,12 +454,33 @@ impl Config { } } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum ConfigError { - #[error("io error while reading config: {0}")] - Io(#[from] io::Error), - #[error("config toml parsing error: {0}")] - Toml(#[from] toml::de::Error), + Io(io::Error), + Toml(toml::de::Error), +} + +impl std::error::Error for ConfigError {} + +impl Display for ConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Io(e) => write!(f, "io error while reading config: {e}"), + Self::Toml(e) => write!(f, "config toml parsing error: {e}"), + } + } +} + +impl From<io::Error> for ConfigError { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl From<toml::de::Error> for ConfigError { + fn from(value: toml::de::Error) -> Self { + Self::Toml(value) + } } #[cfg(test)] diff --git a/ntpd/src/daemon/config/subnet.rs b/ntpd/src/daemon/config/subnet.rs index e35c83132..bfc789f36 100644 --- a/ntpd/src/daemon/config/subnet.rs +++ b/ntpd/src/daemon/config/subnet.rs @@ -1,6 +1,8 @@ use serde::{de, Deserialize, Deserializer}; -use std::net::{AddrParseError, IpAddr}; -use thiserror::Error; +use std::{ + fmt::Display, + net::{AddrParseError, IpAddr}, +}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct IpSubnet { @@ -8,16 +10,31 @@ pub struct IpSubnet { pub mask: u8, } -#[derive(Debug, Clone, PartialEq, Eq, Error)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum SubnetParseError { - #[error("Invalid subnet syntax")] Subnet, - #[error("{0} in subnet")] - Ip(#[from] AddrParseError), - #[error("Invalid subnet mask")] + Ip(AddrParseError), Mask, } +impl std::error::Error for SubnetParseError {} + +impl Display for SubnetParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Subnet => write!(f, "Invalid subnet syntax"), + Self::Ip(e) => write!(f, "{e} in subnet"), + Self::Mask => write!(f, "Invalid subnet mask"), + } + } +} + +impl From<AddrParseError> for SubnetParseError { + fn from(value: AddrParseError) -> Self { + Self::Ip(value) + } +} + impl std::str::FromStr for IpSubnet { type Err = SubnetParseError; diff --git a/ntpd/src/daemon/spawn/dummy.rs b/ntpd/src/daemon/spawn/dummy.rs index 56c1548b2..37b313942 100644 --- a/ntpd/src/daemon/spawn/dummy.rs +++ b/ntpd/src/daemon/spawn/dummy.rs @@ -1,4 +1,4 @@ -use std::net::SocketAddr; +use std::{fmt::Display, net::SocketAddr}; use super::{ BasicSpawner, PeerCreateParameters, PeerRemovedEvent, SpawnAction, SpawnEvent, SpawnerId, @@ -11,9 +11,17 @@ pub struct DummySpawner { to_activate: isize, } -#[derive(Debug, thiserror::Error)] +#[derive(Debug)] pub enum DummySpawnerError {} +impl Display for DummySpawnerError { + fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unreachable!() + } +} + +impl std::error::Error for DummySpawnerError {} + impl DummySpawner { pub fn new(to_spawn: Vec<PeerCreateParameters>, keep_active: usize) -> DummySpawner { DummySpawner { diff --git a/ntpd/src/daemon/spawn/nts.rs b/ntpd/src/daemon/spawn/nts.rs index 9e2fb2280..ae1b97a19 100644 --- a/ntpd/src/daemon/spawn/nts.rs +++ b/ntpd/src/daemon/spawn/nts.rs @@ -1,7 +1,7 @@ +use std::fmt::Display; use std::net::SocketAddr; use std::ops::Deref; -use thiserror::Error; use tokio::sync::mpsc; use tracing::warn; @@ -15,10 +15,25 @@ pub struct NtsSpawner { has_spawned: bool, } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum NtsSpawnError { - #[error("Channel send error: {0}")] - SendError(#[from] mpsc::error::SendError<SpawnEvent>), + SendError(mpsc::error::SendError<SpawnEvent>), +} + +impl std::error::Error for NtsSpawnError {} + +impl Display for NtsSpawnError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SendError(e) => write!(f, "Channel send error: {e}"), + } + } +} + +impl From<mpsc::error::SendError<SpawnEvent>> for NtsSpawnError { + fn from(value: mpsc::error::SendError<SpawnEvent>) -> Self { + Self::SendError(value) + } } pub(super) async fn resolve_addr(address: (&str, u16)) -> Option<SocketAddr> { diff --git a/ntpd/src/daemon/spawn/nts_pool.rs b/ntpd/src/daemon/spawn/nts_pool.rs index 65a3b26b0..95b62fa27 100644 --- a/ntpd/src/daemon/spawn/nts_pool.rs +++ b/ntpd/src/daemon/spawn/nts_pool.rs @@ -1,6 +1,6 @@ +use std::fmt::Display; use std::ops::Deref; -use thiserror::Error; use tokio::sync::mpsc; use tracing::warn; @@ -23,10 +23,25 @@ pub struct NtsPoolSpawner { current_peers: Vec<PoolPeer>, } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum NtsPoolSpawnError { - #[error("Channel send error: {0}")] - SendError(#[from] mpsc::error::SendError<SpawnEvent>), + SendError(mpsc::error::SendError<SpawnEvent>), +} + +impl std::error::Error for NtsPoolSpawnError {} + +impl Display for NtsPoolSpawnError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SendError(e) => write!(f, "Channel send error: {e}"), + } + } +} + +impl From<mpsc::error::SendError<SpawnEvent>> for NtsPoolSpawnError { + fn from(value: mpsc::error::SendError<SpawnEvent>) -> Self { + Self::SendError(value) + } } impl NtsPoolSpawner { diff --git a/ntpd/src/daemon/spawn/pool.rs b/ntpd/src/daemon/spawn/pool.rs index f4bd659b7..7b8431946 100644 --- a/ntpd/src/daemon/spawn/pool.rs +++ b/ntpd/src/daemon/spawn/pool.rs @@ -1,7 +1,7 @@ +use std::fmt::Display; use std::{net::SocketAddr, ops::Deref}; use ntp_proto::ProtocolVersion; -use thiserror::Error; use tokio::sync::mpsc; use tracing::warn; @@ -21,9 +21,17 @@ pub struct PoolSpawner { known_ips: Vec<SocketAddr>, } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum PoolSpawnError {} +impl Display for PoolSpawnError { + fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + unreachable!() + } +} + +impl std::error::Error for PoolSpawnError {} + impl PoolSpawner { pub fn new(config: PoolPeerConfig) -> PoolSpawner { PoolSpawner { diff --git a/ntpd/src/daemon/spawn/standard.rs b/ntpd/src/daemon/spawn/standard.rs index 467a22e19..1a79390bf 100644 --- a/ntpd/src/daemon/spawn/standard.rs +++ b/ntpd/src/daemon/spawn/standard.rs @@ -1,7 +1,7 @@ +use std::fmt::Display; use std::{net::SocketAddr, ops::Deref}; use ntp_proto::ProtocolVersion; -use thiserror::Error; use tokio::sync::mpsc; use tracing::warn; @@ -18,12 +18,27 @@ pub struct StandardSpawner { has_spawned: bool, } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum StandardSpawnError { - #[error("Channel send error: {0}")] - SendError(#[from] mpsc::error::SendError<SpawnEvent>), + SendError(mpsc::error::SendError<SpawnEvent>), } +impl Display for StandardSpawnError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SendError(e) => write!(f, "Channel send error: {e}"), + } + } +} + +impl From<mpsc::error::SendError<SpawnEvent>> for StandardSpawnError { + fn from(value: mpsc::error::SendError<SpawnEvent>) -> Self { + Self::SendError(value) + } +} + +impl std::error::Error for StandardSpawnError {} + impl StandardSpawner { pub fn new(config: StandardPeerConfig) -> StandardSpawner { StandardSpawner { diff --git a/nts-pool-ke/Cargo.toml b/nts-pool-ke/Cargo.toml index 8f9f69a3d..6b8de61ff 100644 --- a/nts-pool-ke/Cargo.toml +++ b/nts-pool-ke/Cargo.toml @@ -11,7 +11,7 @@ publish.workspace = true rust-version.workspace = true [dependencies] -tokio = { workspace = true, features = ["rt-multi-thread", "io-util", "io-std", "fs", "sync", "net", "macros", "time"] } +tokio = { workspace = true, features = ["rt-multi-thread", "io-util", "fs", "net", "macros", "time" ] } toml.workspace = true tracing.workspace = true tracing-subscriber = { version = "0.3.0", default-features = false, features = ["std", "fmt", "ansi"] } @@ -19,7 +19,6 @@ rustls.workspace = true rustls-pemfile.workspace = true rustls-native-certs.workspace = true serde.workspace = true -thiserror.workspace = true ntp-proto = { workspace = true, features = ["nts-pool"] } tokio-rustls.workspace = true diff --git a/nts-pool-ke/src/config.rs b/nts-pool-ke/src/config.rs index bac65bcdd..b8eb8034f 100644 --- a/nts-pool-ke/src/config.rs +++ b/nts-pool-ke/src/config.rs @@ -1,11 +1,11 @@ use std::{ + fmt::Display, net::SocketAddr, os::unix::fs::PermissionsExt, path::{Path, PathBuf}, }; use serde::Deserialize; -use thiserror::Error; use tracing::{info, warn}; #[derive(Deserialize, Debug)] @@ -16,14 +16,35 @@ pub struct Config { pub observability: ObservabilityConfig, } -#[derive(Error, Debug)] +#[derive(Debug)] pub enum ConfigError { - #[error("io error while reading config: {0}")] - Io(#[from] std::io::Error), - #[error("config toml parsing error: {0}")] - Toml(#[from] toml::de::Error), + Io(std::io::Error), + Toml(toml::de::Error), } +impl From<std::io::Error> for ConfigError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + +impl From<toml::de::Error> for ConfigError { + fn from(value: toml::de::Error) -> Self { + Self::Toml(value) + } +} + +impl Display for ConfigError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Io(e) => write!(f, "io error while reading config: {e}"), + Self::Toml(e) => write!(f, "config toml parsing error: {e}"), + } + } +} + +impl std::error::Error for ConfigError {} + impl Config { pub fn check(&self) -> bool { true