whee~ I think this is decent one

work
Avril 4 years ago
parent 3d254efc4b
commit cf6d97d69f
Signed by: flanchan
GPG Key ID: 284488987C31F630

225
Cargo.lock generated

@ -1,5 +1,38 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "0.4.7" version = "0.4.7"
@ -12,6 +45,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.12.3" version = "0.12.3"
@ -56,6 +103,32 @@ dependencies = [
"time", "time",
] ]
[[package]]
name = "color-eyre"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028eb0f4472a78c8f7b82751eedb5326dcb2a00c8ba89454e2d0df819d856e2c"
dependencies = [
"ansi_term 0.12.1",
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a99aa4aa18448eef4c7d3f86d2720d2d8cad5c860fe9ff9b279293efdc8f5be"
dependencies = [
"ansi_term 0.11.0",
"tracing-core",
"tracing-error",
]
[[package]] [[package]]
name = "cpuid-bool" name = "cpuid-bool"
version = "0.1.2" version = "0.1.2"
@ -71,6 +144,16 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "eyre"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0f9683839e579a53258d377fcc0073ca0bf2042ac5e6c60a598069e64403a6d"
dependencies = [
"indenter",
"once_cell",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -109,6 +192,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.15" version = "0.1.15"
@ -118,6 +207,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "indenter"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0bd112d44d9d870a6819eb505d04dd92b5e4d94bb8c304924a0872ae7016fb5"
[[package]] [[package]]
name = "iovec" name = "iovec"
version = "0.1.4" version = "0.1.4"
@ -137,6 +232,12 @@ dependencies = [
"winapi-build", "winapi-build",
] ]
[[package]]
name = "lazy_format"
version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f389fd31a70d3b0f1d9d80e8f7a9366127fe5fb819f4d4dd21122a167352da8f"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -164,6 +265,15 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
[[package]]
name = "miniz_oxide"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
dependencies = [
"adler",
]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.6.22" version = "0.6.22"
@ -268,6 +378,18 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
[[package]]
name = "once_cell"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.3.0" version = "0.3.0"
@ -304,6 +426,36 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "sha2" name = "sha2"
version = "0.9.1" version = "0.9.1"
@ -317,6 +469,15 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "sharded-slab"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06d5a3f5166fb5b42a5439f2eee8b9de149e235961e3eb21c5808fc3ea17ff3e"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.2.1" version = "1.2.1"
@ -356,6 +517,15 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.43"
@ -401,6 +571,58 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tracing"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
dependencies = [
"cfg-if",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5"
dependencies = [
"lazy_static",
]
[[package]]
name = "tracing-error"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abd165311cc4d7a555ad11cc77a37756df836182db0d81aac908c8184c584f40"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.12.0" version = "1.12.0"
@ -426,6 +648,9 @@ dependencies = [
"base64", "base64",
"cfg-if", "cfg-if",
"chrono", "chrono",
"color-eyre",
"lazy_format",
"rustc_version",
"sha2", "sha2",
"tokio", "tokio",
] ]

@ -22,3 +22,8 @@ base64 = {version = "0.12", optional = true}
cfg-if = "0.1.10" cfg-if = "0.1.10"
sha2 = "0.9.1" sha2 = "0.9.1"
chrono = "0.4.13" chrono = "0.4.13"
color-eyre = "0.5.1"
lazy_format = "1.8.3"
[build-dependencies]
rustc_version = "0.2"

@ -0,0 +1,24 @@
extern crate rustc_version;
use rustc_version::{version, version_meta, Channel};
fn main() {
// Assert we haven't travelled back in time
assert!(version().unwrap().major >= 1);
// Set cfg flags depending on release channel
match version_meta().unwrap().channel {
Channel::Stable => {
println!("cargo:rustc-cfg=stable");
}
Channel::Beta => {
println!("cargo:rustc-cfg=beta");
}
Channel::Nightly => {
println!("cargo:rustc-cfg=nightly");
}
Channel::Dev => {
println!("cargo:rustc-cfg=dev");
}
}
}

@ -1,323 +0,0 @@
//! Error handling stuffs
use std::{
error,
fmt,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
pub struct Context
{
file: Option<&'static str>,
line: Option<u32>,
column: Option<u32>,
}
impl Context
{
pub fn with_all(file: &'static str, line: u32, column: u32) -> Self
{
Self {
file: Some(file),
line: Some(line),
column: Some(column)
}
}
}
impl fmt::Display for Context
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if let Some(file) = self.file {
write!(f, "{} ", file)
} else {
write!(f, "? ")
}?;
if let Some(line) = self.line {
write!(f, "{}:", line)
} else {
write!(f, "?:")
}?;
if let Some(column) = self.column {
write!(f, "{}", column)
} else {
write!(f, "?")
}
}
}
#[macro_export] macro_rules! context {
() => ($crate::error::Context::with_all(file!(), line!(), column!()));
}
#[derive(Debug)]
pub struct ContextError<E>(E, Context)
where E: error::Error;
impl<E> ContextError<E>
where E: error::Error
{
pub fn with_context(from: E, ctx: Context) -> Self
{
Self(from, ctx)
}
}
impl<E> error::Error for ContextError<E>
where E: error::Error + 'static
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(&self.0)
}
}
impl<E> fmt::Display for ContextError<E>
where E: error::Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}: {}", self.1, self.0)
}
}
#[macro_export] macro_rules! error {
($msg:expr) => {
{
pub struct DisplayError<D>(D) where D: fmt::Display;
impl<D> fmt::Debug for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", std::any::type_name::<Self>())
}
}
impl<D> fmt::Display for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(f)
}
}
impl<D> error::Error for DisplayError<D>
where D: fmt::Display{}
$crate::error::ContextError::with_context(DisplayError($msg), context!())
}
};
(&fmt:literal $($tt:tt)*) => ($crate::error!(format!($literal $($tt)*)));
}
pub struct Wrap<E,D>(E,D);
impl<E,D> Wrap<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
pub fn long_message(&self) -> impl fmt::Display + '_
{
pub struct LongMessage<'a, E,D>(&'a Wrap<E,D>);
impl<'a, E,D> fmt::Display for LongMessage<'a,E,D>
where E: error::Error + 'static,
D: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use error::Error;
for (num, next) in (1..).zip(std::iter::successors(self.0.source(), |e| e.source()))
{
writeln!(f, " [{}] -> {}", num, next)?;
}
Ok(())
}
}
LongMessage(self)
}
}
impl<E,D> fmt::Display for Wrap<E,D>
where D: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.1)
}
}
impl<E,D> fmt::Debug for Wrap<E,D>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", std::any::type_name::<Self>())
}
}
impl<E,D> error::Error for Wrap<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(&self.0)
}
}
pub trait WrapErrExt<T,E>: Sized
{
#[inline] fn wrap_err<F,D>(self, val: D) -> Result<T, Wrap<E, D>>
where D: fmt::Display
{
self.wrap_err_with(|| val)
}
fn wrap_err_with<F,D>(self, func: F) -> Result<T, Wrap<E, D>>
where D: fmt::Display,
F: FnOnce() -> D;
}
impl<T,E> WrapErrExt<T,E> for Result<T,E>
{
fn wrap_err_with<F,D>(self, func: F) -> Result<T, Wrap<E, D>>
where D: fmt::Display,
F: FnOnce() -> D
{
self.map_err(|e| Wrap(e, func()))
}
}
// We can now define the reporter trait, like last time, and then out own reporter type for `Wrap<E,D>` that prints its long message properly
pub trait Reporter
{
type Error: error::Error + ?Sized;
#[inline] fn long_message(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result
{
Ok(())
}
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn source(&self) -> &Self::Error;
}
impl<E> Reporter for Box<E>
where E: error::Error + ?Sized
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self)
}
fn source(&self) -> &Self::Error
{
self
}
}
pub struct WrapReporter<E,D>(Wrap<E,D>);
impl<E,D> Reporter for WrapReporter<E,D>
where E: error::Error + 'static,
D: fmt::Display
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
fn long_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0.long_message())
}
fn source(&self) -> &Self::Error
{
&self.0.0
}
}
impl<E,D> From<Wrap<E,D>> for WrapReporter<E,D>
{
fn from(from: Wrap<E,D>) -> Self
{
Self(from)
}
}
pub struct Report<E>(Box<E>) where E: ?Sized;
impl<E> From<E> for Report<E>
where E: error::Error
{
fn from(from: E) -> Self
{
Self(Box::new(from))
}
}
impl<E> From<Box<E>> for Report<E>
where E: error::Error + 'static + ?Sized
{
fn from(from: Box<E>) -> Self
{
Self(from)
}
}
impl<E> Reporter for Report<E>
where E: error::Error + ?Sized
{
type Error = E;
fn short_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.0)
}
fn source(&self) -> &Self::Error
{
&self.0
}
}
#[cfg(test)]
mod test
{
use super::*;
#[test]
fn test()
{
let err: Result<(), _> = Err(error!("hello world"))
.wrap_err_with(|| error!("thing failed"))
.wrap_err_with(|| error!("top level failed again"));
if let Err(err) = err
{
panic!("{}\n\n{}", err, err.long_message());
}
}
#[test]
fn report()
{
fn one() -> Result<(), Report<dyn error::Error>>
{
let err: Result<(), _> = Err(error!("hello world"))
.wrap_err_with(|| error!("thing failed"))
.wrap_err_with(|| error!("top level failed again"));
Ok(err?) //failed again. just fucking use eyre
}
}
}

@ -0,0 +1,119 @@
//! Aggregate errors
use super::*;
use std::result::Result;
/// An aggregate of 2 operations that *might* have failed.
///
/// In order for all aggregate operations to propagate a value, they must all succeed.
/// However, will return the errors of failed ones and continue propagating an `Err()` variant, so you get any of the error(s) from the previous aggregate calls.
///
/// This produces a potential tree of errors, which the `Error` trait cannot represent, so the reporting for `Aggregate` is a bit shit so far, but its general rule is:
/// * Debug formatter prints all of the first and second errors including their sub-errors. So, the entire tree. But in an ugly and hard to read way
/// * The default formatter prints only the *first* error, but also only if the second error exists, because:
/// * The `source()` for the aggregate is first the *second* error, if exists, then the *first* if not.
///
/// This means, `Aggregate`'s message itself when it does form a tree will be the first branch of the tree, and its source will be the second branch. The second branch is signigicanly better formatted by `eyre`, so the results from the first branch might be a bit hard to decipher, but it's fit for purpose in smaller chains.
///
/// We prioratise the second branch as the source because that's the one least likely to be an `Aggregate` itself, and will have a more meaningful message trace when reported by `eyre`.
pub struct Aggregate<T,U>(Option<T>, Option<U>);
pub trait AggregateExt<T,E>: Sized
{
/// Aggregate this result with a function that can take the value if suceeded and return its own value, or a reference to the error and return either its own error or *both*
///
/// # How it works
/// `Operation 2` is always ran, and gets the value from `operation 1` if it suceeded.
/// There is never a time where the closure will not be called except when there is a panic
///
/// * If `operation 1` and `2` fail: The aggregate containing both errors returns
/// * If `operation 1` succeeds but operation 2 does not: the error from `operation 2` is returned
/// * If `operation 2` succeeds but `operation 1` does not: the error from `operation 1` is returned
/// * If both succeed: the value from `operation 2` is returned.
fn aggregate_with<F,U,V>(self, next: F) -> Result<V, Aggregate<E, U>>
where F: FnOnce(Result<T,&E>) -> Result<V, U>;
/// Same as `aggregate_with` except it takes a result instead of taking a closure, so it is evaluated before the aggregation is called. In practive this has little difference, because all closures are called regardless, however it can help in certain situation where we don't need to know the value of pervious aggregate calls, or if we want to calls to happen even if there is a panic in one of the aggregations.
fn aggregate<U,V>(self, next: Result<V,U>) -> Result<(T,V), Aggregate<E,U>>;
}
impl<T,E> AggregateExt<T,E> for Result<T,E>
{
fn aggregate<U,V>(self, next: Result<V,U>) -> Result<(T,V), Aggregate<E,U>>
{
match self {
Ok(v) => match next {
Ok(v1) => Ok((v,v1)),
Err(err) => Err(Aggregate(None, Some(err))),
},
Err(er) => match next {
Ok(_) => Err(Aggregate(Some(er), None)),
Err(err) => Err(Aggregate(Some(er), Some(err))),
},
}
}
fn aggregate_with<F,U,V>(self, next: F) -> Result<V, Aggregate<E, U>>
where F: FnOnce(Result<T,&E>) -> Result<V, U>
{
match self {
Ok(v) => {
match next(Ok(v)) {
Err(err) => Err(Aggregate(None, Some(err))),
Ok(ok) => Ok(ok),
}
}
Err(e) => {
match next(Err(&e)) {
Err(err) => Err(Aggregate(Some(e), Some(err))),
Ok(_) => Err(Aggregate(Some(e), None)),
}
}
}
}
}
impl<T,U> fmt::Debug for Aggregate<T,U>
where T: fmt::Debug,
U: fmt::Debug
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Aggregate Error ({:?}, {:?})", self.0, self.1)
}
}
impl<T,U> fmt::Display for Aggregate<T,U>
where T: error::Error,
U: error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Aggregate Error ({})", self.0.as_ref().map(|_| 1).unwrap_or(0) + self.1.as_ref().map(|_| 1).unwrap_or(0))?;
if self.1.is_some() {
if let Some(one) = &self.0 {
writeln!(f, "\nBranch: {}", one)?;
for next in std::iter::successors(one.source(), |e| e.source())
{
writeln!(f, " -> {}", next)?;
}
}
}
Ok(())
}
}
impl<T,U> error::Error for Aggregate<T,U>
where T: error::Error + 'static,
U: error::Error + 'static,
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
if let Some(z) = &self.1 {
return Some(z);
}
if let Some(o) = &self.0 {
return Some(o);
}
None
}
}

@ -0,0 +1,71 @@
//! Error contexts
use super::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
pub struct Context
{
file: Option<&'static str>,
line: Option<u32>,
column: Option<u32>,
}
impl Context
{
/// Create a new populated context
pub const fn with_all(file: &'static str, line: u32, column: u32) -> Self
{
Self {
file: Some(file),
line: Some(line),
column: Some(column)
}
}
pub fn is_empty(&self) -> bool
{
self.column.is_none() &&
self.line.is_none() &&
self.file.is_none()
}
}
impl fmt::Display for Context
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if self.is_empty() {
write!(f, "<unbound>")
} else {
if let Some(file) = self.file {
write!(f, "{} ", file)
} else {
write!(f, "? ")
}?;
if let Some(line) = self.line {
write!(f, "{}:", line)
} else {
write!(f, "?:")
}?;
if let Some(column) = self.column {
write!(f, "{}", column)
} else {
write!(f, "?")
}
}
}
}
pub struct ContextMessage<D>(pub(super) D, pub(super) Context)
where D: fmt::Display;
impl<D> ContextMessage<D>
where D: fmt::Display
{
pub const fn new(ctx: Context, msg: D) -> Self
{
Self(msg, ctx)
}
}

@ -0,0 +1,113 @@
//! Message string
use super::*;
use std::result::Result;
pub enum Message
{
Str(&'static str),
String(String),
Other(Box<dyn fmt::Display + Send + Sync + 'static>),
None,
}
impl cmp::PartialEq for Message
{
fn eq(&self, other: &Self) -> bool
{
self.as_str() == other.as_str()
}
}
impl Message
{
/// Into the internal string
pub fn into_string(self) -> Option<Cow<'static, str>>
{
Some(match self {
Self::Str(string) => Cow::Borrowed(string),
Self::String(string) => Cow::Owned(string),
Self::Other(other) => Cow::Owned(other.to_string()),
_ => return None,
})
}
/// Returns message as a string, either created from `format!`, or referenced from inner
pub fn as_str(&self) -> Option<Cow<'_, str>>
{
Some(match self {
Self::Str(string) => Cow::Borrowed(string),
Self::String(string) => Cow::Borrowed(&string),
Self::Other(other) => Cow::Owned(other.to_string()),
_ => return None,
})
}
pub const fn from_str(string: &'static str) -> Self
{
Self::Str(string)
}
pub const fn from_string(string: String) -> Self
{
Self::String(string)
}
pub fn from_other(other: impl fmt::Display + Send + Sync + 'static) -> Self
{
Self::Other(Box::new(other))
}
pub const fn none() -> Self
{
Self::None
}
pub fn fmt(&self) -> impl fmt::Display + '_
{
struct Format<'a>(&'a Message);
impl<'a> fmt::Display for Format<'a>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self.0 {
Message::Str(string) => write!(f, "{}", string),
Message::String(string) => write!(f, "{}", string),
Message::Other(other) => write!(f, "{}", other),
_ => Ok(())
}
}
}
Format(&self)
}
}
impl FromStr for Message
{
type Err = !;
fn from_str(string: &str) -> Result<Self, Self::Err>
{
Ok(Self::String(string.to_string()))
}
}
impl<T> From<T> for Message
where T: fmt::Display + 'static + Send + Sync
{
fn from(from: T) -> Self
{
Self::from_other(from)
}
}
impl fmt::Debug for Message
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "error::Message (")?;
match self {
Self::Str(string) => write!(f, "{:?}", string),
Self::String(string) => write!(f, "{:?}", string),
Self::Other(_) => write!(f, "<opaque type>"),
_=> write!(f, "<unbound>"),
}?;
write!(f,")")
}
}

@ -0,0 +1,282 @@
//! Error handling stuffs (I think we'll go with this one in the end)
use std::{
error,
io,
cmp,
fmt,
str::{
FromStr,
},
borrow::Cow,
marker::{
Send, Sync
},
};
/// Result for all our operations, see `ErrorKind` below
///
/// This type can be used for when we want to propagate an error, but not pass it off to `eyre` reporter just yet, because we might still want to act on it and respond.
pub type Result<T> = std::result::Result<T, Error>;
mod context;
pub use context::*;
mod message;
use message::*;
mod wrap;
pub use wrap::*;
mod aggregate;
pub use aggregate::*;
impl Default for ErrorKind
{
#[inline]
fn default() -> Self
{
Self::None
}
}
impl Error
{
pub fn into_inner(self) -> ErrorKind
{
*self.internal
}
/// The kind of the error
pub fn kind(&self) -> &ErrorKind
{
&self.internal
}
/// The message for this error
pub fn message(&self) -> Option<Cow<'_, str>>
{
self.message.as_str()
}
/// The context from this error
pub fn context(&self) -> &Context
{
&self.context
}
/// Create a new error with a constant string
pub fn new_static(err: ErrorKind, ctx: Context, message: &'static str) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_str(message),
}
}
/// Create a new error with a string
pub fn new_string(err: ErrorKind, ctx: Context, message: String) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_string(message),
}
}
/// Create a new error with a message
pub fn new(err: ErrorKind, ctx: Context, message: impl fmt::Display + 'static + Send + Sync) -> Self
{
Self {
internal: box err,
context: ctx,
message: Message::from_other(message),
}
}
}
impl fmt::Display for Error
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
if !self.context().is_empty() {
write!(f, "{}: ", self.context)?;
}
match &self.message {
Message::None => write!(f, "{}", self.internal),
message => write!(f, "{}", message.fmt())
}
}
}
impl error::Error for Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
match &self.message {
Message::None => std::iter::successors(self.internal.source(), |e| e.source()).take(1).next(),
_ => Some(&self.internal),
}
}
}
#[macro_export] macro_rules! context {
() => ($crate::error::Context::with_all(file!(), line!(), column!()));
($msg:expr) => ($crate::error::ContextMessage::new($crate::context!(), $msg));
(yield $fmt:literal $($tt:tt)*) => ($crate::context!(lazy_format::lazy_format!($fmt $($tt)*)));
($fmt:literal $($tt:tt)*) => ($crate::context!(format!($fmt $($tt)*)));
}
#[macro_export] macro_rules! error {
($kind:expr, $lit:literal) => ($crate::error::Error::new_static($kind, $crate::context!(), $lit));
($lit:literal) => ($crate::error!(Default::default(), $lit));
($kind:expr, $msg:expr) => {
{
pub struct DisplayError<D>(D) where D: fmt::Display;
impl<D> fmt::Debug for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", std::any::type_name::<Self>())
}
}
impl<D> fmt::Display for DisplayError<D>
where D: fmt::Display
{
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
self.0.fmt(f)
}
}
impl<D> error::Error for DisplayError<D>
where D: fmt::Display{}
$crate::error::Error::new($kind, $crate::context!(), DisplayError($msg))
}
};
($msg:expr) => ($crate::error!(Default::default(), $msg));
(yield $kind:expr, $lit:literal $($tt:tt)*) => ($crate::error!($kind, lazy_format::lazy_format!($lit $($tt)*)));
($kind:expr, $lit:literal $($tt:tt)*) => ($crate::error!($kind, format!($lit $($tt)*)));
(yield $lit:literal $($tt:tt)*) => ($crate::error!(yield Default::default(), $lit $($tt)*));
($lit:literal $($tt:tt)*) => ($crate::error!(Default::default(), $lit $($tt)*));
}
#[derive(Debug)]
pub struct Error
{
internal: Box<ErrorKind>,
context: Context,
message: Message,
}
// Actual stuffs
#[non_exhaustive]
#[derive(Debug)]
pub enum ErrorKind
{
IO(io::Error),
Wrap(Error),
Any(Box<dyn error::Error +Send + Sync + 'static>),
None,
Unknown,
}
impl fmt::Display for ErrorKind
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::IO(io) => write!(f, "i/o error: {}", io),
Self::Wrap(wrap) => write!(f, "{}", wrap),
Self::None => Ok(()),
_ => write!(f, "unknown error"),
}
}
}
impl error::Error for ErrorKind
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(match &self {
Self::IO(io) => io,
Self::Wrap(wrap) => return wrap.source(),
_ => return None,
})
}
}
impl From<io::Error> for ErrorKind
{
fn from(from: io::Error) -> Self
{
Self::IO(from)
}
}
impl<T> From<T> for Error
where T: Into<ErrorKind>
{
fn from(from: T) -> Self
{
Self {
internal: box from.into(),
context: Default::default(),
message: Message::None,
}
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod test
{
use super::*;
use color_eyre::{
eyre::{
Result as ReportedResult,
},
Help,
SectionExt,
};
#[test]
fn test() -> ReportedResult<()>
{
color_eyre::install()?;
fn testt() -> Result<()> {
let err= error!(yield one().unwrap_err().into_inner(), "hello world {} {}", 1, 2);
Err(err)
.wrap_err_with(|| context!("with string"))
.wrap_err_with(|| context!(yield "with another string and some stuffs {}", 1))?;
Ok(())
}
fn works() -> Result<()>
{
Ok(())
}
fn one() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "end of records"))?;
Ok(())
}
fn two() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "nonexistent"))?;
Ok(())
}
fn three() -> Result<()>
{
Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "piped"))
.wrap_err_with(|| context!("hello hello"))?;
Ok(())
}
Ok(one()
.aggregate_with(|_| two())
.aggregate_with(|_| works())
.aggregate_with(|_| three())?)
}
}

@ -0,0 +1,117 @@
//! Wrapping errors
use super::*;
use std::result::Result;
/// Convenienve constant for `map_msg`, used so you don't need to keep going `Option::<!>::None`.
pub const NO_MESSAGE: Option<!> = None;
/// Traits for assigning messages and context to any error that can be transformed into `ErrorKind`.
pub trait Assign<T>: Sized
{
/// Wrap an error with a context message deferred
fn wrap_err_with<F,D>(self, with: F) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> ContextMessage<D>;
/// Wrap an error with a context message
#[inline] fn wrap_err<D>(self, with: ContextMessage<D>) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static
{
self.wrap_err_with(|| with)
}
/// Wrap an error with a message and no context
#[inline] fn wrap_err_no_context<D>(self, with: D) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static
{
self.wrap_err(ContextMessage::new(Default::default(), with))
}
/// Wrap an error with a message and no context deferred
#[inline] fn wrap_err_with_no_context<F, D>(self, with: F) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> D
{
self.wrap_err_with(|| ContextMessage::new(Default::default(), with()))
}
}
impl<T> Assign<T> for Result<T,Error>
{
fn wrap_err_with<F,D>(self, with: F) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> ContextMessage<D>
{
self.map_err(|e| {
let val = with();
Error::new(ErrorKind::Wrap(e), val.1, val.0)
})
}
}
impl<T,E> Assign<T> for Result<T,E>
where E: Into<ErrorKind>
{
fn wrap_err_with<F,D>(self, with: F) -> Result<T, Error>
where D: fmt::Display + Send + Sync + 'static,
F: FnOnce() -> ContextMessage<D>
{
self.map_err(|e| {
let val = with();
Error::new(e.into(), val.1, val.0)
})
}
}
/// Error mapping extensions
pub trait WrapErr<T>: Sized
{
/// Map the error kind
fn map_kind_with<F: FnOnce(ErrorKind) -> ErrorKind>(self, kind: F) -> Result<T,Error>;
/// Map the error kind impeatively
#[inline] fn map_kind(self, kind: ErrorKind) -> Result<T, Error>
{
self.map_kind_with(|_| kind)
}
/// Map the error message
fn map_msg_with<M, F: FnOnce(Option<Cow<'static, str>>) -> Option<M>>(self, msg: F) -> Result<T, Error>
where M: fmt::Display + Send + Sync + 'static;
/// Map the error message
#[inline] fn map_msg<M>(self, msg: M) -> Result<T, Error>
where M: fmt::Display + Send + Sync + 'static
{
self.map_msg_with(|_| Some(msg))
}
#[inline] fn map_msg_remove(self) -> Result<T, Error>
{
self.map_msg_with(|_| NO_MESSAGE)
}
}
impl<T> WrapErr<T> for Result<T,Error>
{
fn map_kind_with<F: FnOnce(ErrorKind) -> ErrorKind>(self, kind: F) -> Result<T,Error>
{
self.map_err(|err| {
Error {
internal: box kind(*err.internal),
..err
}
})
}
fn map_msg_with<M, F: FnOnce(Option<Cow<'static, str>>) -> Option<M>>(self, msg: F) -> Result<T, Error>
where M: fmt::Display + Send + Sync + 'static
{
self.map_err(|err| {
let msg = msg(err.message.into_string());
Error {
message: match msg {
Some(msg) => Message::from_other(msg),
None => Message::None,
},
..err
}
})
}
}

@ -1,3 +1,6 @@
#![cfg_attr(nightly, feature(never_type))]
#![cfg_attr(nightly, feature(box_syntax))]
#![cfg_attr(nightly, feature(const_fn))]
#![allow(dead_code)] #![allow(dead_code)]
@ -7,6 +10,20 @@ mod ext;
use ext::*; use ext::*;
mod error; mod error;
#[allow(unused_imports)]
use error::{
WrapErr as _,
Assign as _,
Result as VidelResult,
};
#[allow(unused_imports)]
use color_eyre::{
eyre::{
self,
Result as ReportResult,
},
};
mod consts; mod consts;
mod util; mod util;
mod hash; mod hash;

Loading…
Cancel
Save