mirror of https://github.com/zkat/miette.git
Add dynamic diagnostic (#262)
This commit is contained in:
parent
675f3411e3
commit
024145dbdd
|
|
@ -43,7 +43,11 @@ jobs:
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --all -- -D warnings
|
run: cargo clippy --all -- -D warnings
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
if: matrix.rust == 'stable'
|
||||||
run: cargo test --all --verbose --features fancy
|
run: cargo test --all --verbose --features fancy
|
||||||
|
- name: Run tests
|
||||||
|
if: matrix.rust == '1.56.0'
|
||||||
|
run: cargo test --all --verbose --features fancy no-format-args-capture
|
||||||
|
|
||||||
miri:
|
miri:
|
||||||
name: Miri
|
name: Miri
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
Thank you so much for your interest in contributing!. All types of contributions are encouraged and valued. See the [table of contents](#toc) for different ways to help and details about how this project handles them!📝
|
Thank you so much for your interest in contributing! All types of contributions are encouraged and valued. See the [table of contents](#toc) for different ways to help and details about how this project handles them!📝
|
||||||
|
|
||||||
Please make sure to read the relevant section before making your contribution! It will make it a lot easier for us maintainers to make the most of it and smooth out the experience for all involved. 💚
|
Please make sure to read the relevant section before making your contribution! It will make it a lot easier for us maintainers to make the most of it and smooth out the experience for all involved. 💚
|
||||||
|
|
||||||
|
|
|
||||||
11
Cargo.toml
11
Cargo.toml
|
|
@ -42,7 +42,16 @@ lazy_static = "1.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
fancy-no-backtrace = ["owo-colors", "is-terminal", "textwrap", "terminal_size", "supports-hyperlinks", "supports-color", "supports-unicode"]
|
no-format-args-capture = []
|
||||||
|
fancy-no-backtrace = [
|
||||||
|
"owo-colors",
|
||||||
|
"is-terminal",
|
||||||
|
"textwrap",
|
||||||
|
"terminal_size",
|
||||||
|
"supports-hyperlinks",
|
||||||
|
"supports-color",
|
||||||
|
"supports-unicode",
|
||||||
|
]
|
||||||
fancy = ["fancy-no-backtrace", "backtrace", "backtrace-ext"]
|
fancy = ["fancy-no-backtrace", "backtrace", "backtrace-ext"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
|
||||||
67
README.md
67
README.md
|
|
@ -4,7 +4,7 @@
|
||||||
You run miette? You run her code like the software? Oh. Oh! Error code for
|
You run miette? You run her code like the software? Oh. Oh! Error code for
|
||||||
coder! Error code for One Thousand Lines!
|
coder! Error code for One Thousand Lines!
|
||||||
|
|
||||||
### About
|
## About
|
||||||
|
|
||||||
`miette` is a diagnostic library for Rust. It includes a series of
|
`miette` is a diagnostic library for Rust. It includes a series of
|
||||||
traits/protocols that allow you to hook into its error reporting facilities,
|
traits/protocols that allow you to hook into its error reporting facilities,
|
||||||
|
|
@ -32,7 +32,7 @@ output like in the screenshots above.** You should only do this in your
|
||||||
toplevel crate, as the fancy feature pulls in a number of dependencies that
|
toplevel crate, as the fancy feature pulls in a number of dependencies that
|
||||||
libraries and such might not want.
|
libraries and such might not want.
|
||||||
|
|
||||||
### Table of Contents <!-- omit in toc -->
|
## Table of Contents <!-- omit in toc -->
|
||||||
|
|
||||||
- [About](#about)
|
- [About](#about)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
|
|
@ -47,10 +47,11 @@ libraries and such might not want.
|
||||||
- [... multiple related errors](#-multiple-related-errors)
|
- [... multiple related errors](#-multiple-related-errors)
|
||||||
- [... delayed source code](#-delayed-source-code)
|
- [... delayed source code](#-delayed-source-code)
|
||||||
- [... handler options](#-handler-options)
|
- [... handler options](#-handler-options)
|
||||||
|
- [... dynamic diagnostics](#-dynamic-diagnostics)
|
||||||
- [Acknowledgements](#acknowledgements)
|
- [Acknowledgements](#acknowledgements)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
### Features
|
## Features
|
||||||
|
|
||||||
- Generic [`Diagnostic`] protocol, compatible (and dependent on)
|
- Generic [`Diagnostic`] protocol, compatible (and dependent on)
|
||||||
[`std::error::Error`].
|
[`std::error::Error`].
|
||||||
|
|
@ -75,7 +76,7 @@ the following features:
|
||||||
- Cause chain printing
|
- Cause chain printing
|
||||||
- Turns diagnostic codes into links in [supported terminals](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
|
- Turns diagnostic codes into links in [supported terminals](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
|
||||||
|
|
||||||
### Installing
|
## Installing
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cargo add miette
|
$ cargo add miette
|
||||||
|
|
@ -87,7 +88,7 @@ If you want to use the fancy printer in all these screenshots:
|
||||||
$ cargo add miette --features fancy
|
$ cargo add miette --features fancy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example
|
## Example
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
/*
|
/*
|
||||||
|
|
@ -169,9 +170,9 @@ diagnostic help: Change int or string to be the right types and try again.
|
||||||
diagnostic code: nu::parser::unsupported_operation
|
diagnostic code: nu::parser::unsupported_operation
|
||||||
For more details, see https://docs.rs/nu-parser/0.1.0/nu-parser/enum.ParseError.html#variant.UnsupportedOperation">
|
For more details, see https://docs.rs/nu-parser/0.1.0/nu-parser/enum.ParseError.html#variant.UnsupportedOperation">
|
||||||
|
|
||||||
### Using
|
## Using
|
||||||
|
|
||||||
#### ... in libraries
|
### ... in libraries
|
||||||
|
|
||||||
`miette` is _fully compatible_ with library usage. Consumers who don't know
|
`miette` is _fully compatible_ with library usage. Consumers who don't know
|
||||||
about, or don't want, `miette` features can safely use its error types as
|
about, or don't want, `miette` features can safely use its error types as
|
||||||
|
|
@ -205,7 +206,7 @@ Then, return this error type from all your fallible public APIs. It's a best
|
||||||
practice to wrap any "external" error types in your error `enum` instead of
|
practice to wrap any "external" error types in your error `enum` instead of
|
||||||
using something like [`Report`] in a library.
|
using something like [`Report`] in a library.
|
||||||
|
|
||||||
#### ... in application code
|
### ... in application code
|
||||||
|
|
||||||
Application code tends to work a little differently than libraries. You
|
Application code tends to work a little differently than libraries. You
|
||||||
don't always need or care to define dedicated error wrappers for errors
|
don't always need or care to define dedicated error wrappers for errors
|
||||||
|
|
@ -247,11 +248,11 @@ pub fn some_tool() -> Result<Version> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To construct your own simple adhoc error use the `miette::miette!` macro:
|
To construct your own simple adhoc error use the [`miette!`] macro:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// my_app/lib/my_internal_file.rs
|
// my_app/lib/my_internal_file.rs
|
||||||
use miette::{IntoDiagnostic, Result, WrapErr, miette};
|
use miette::{miette, IntoDiagnostic, Result, WrapErr};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
|
|
||||||
pub fn some_tool() -> Result<Version> {
|
pub fn some_tool() -> Result<Version> {
|
||||||
|
|
@ -262,9 +263,7 @@ pub fn some_tool() -> Result<Version> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There are also similar `miette::bail!` and `miette::ensure!` macros.
|
### ... in `main()`
|
||||||
|
|
||||||
#### ... in `main()`
|
|
||||||
|
|
||||||
`main()` is just like any other part of your application-internal code. Use
|
`main()` is just like any other part of your application-internal code. Use
|
||||||
`Result` as your return value, and it will pretty-print your diagnostics
|
`Result` as your return value, and it will pretty-print your diagnostics
|
||||||
|
|
@ -294,7 +293,7 @@ enabled:
|
||||||
miette = { version = "X.Y.Z", features = ["fancy"] }
|
miette = { version = "X.Y.Z", features = ["fancy"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... diagnostic code URLs
|
### ... diagnostic code URLs
|
||||||
|
|
||||||
`miette` supports providing a URL for individual diagnostics. This URL will
|
`miette` supports providing a URL for individual diagnostics. This URL will
|
||||||
be displayed as an actual link in supported terminals, like so:
|
be displayed as an actual link in supported terminals, like so:
|
||||||
|
|
@ -347,7 +346,7 @@ use thiserror::Error;
|
||||||
struct MyErr;
|
struct MyErr;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... snippets
|
### ... snippets
|
||||||
|
|
||||||
Along with its general error handling and reporting features, `miette` also
|
Along with its general error handling and reporting features, `miette` also
|
||||||
includes facilities for adding error spans/annotations/labels to your
|
includes facilities for adding error spans/annotations/labels to your
|
||||||
|
|
@ -395,8 +394,7 @@ pub struct MyErrorType {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### ... help text
|
#### ... help text
|
||||||
|
|
||||||
`miette` provides two facilities for supplying help text for your errors:
|
`miette` provides two facilities for supplying help text for your errors:
|
||||||
|
|
||||||
The first is the `#[help()]` format attribute that applies to structs or
|
The first is the `#[help()]` format attribute that applies to structs or
|
||||||
|
|
@ -432,7 +430,7 @@ let err = Foo {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... multiple related errors
|
### ... multiple related errors
|
||||||
|
|
||||||
`miette` supports collecting multiple errors into a single diagnostic, and
|
`miette` supports collecting multiple errors into a single diagnostic, and
|
||||||
printing them all together nicely.
|
printing them all together nicely.
|
||||||
|
|
@ -452,7 +450,7 @@ struct MyError {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... delayed source code
|
### ... delayed source code
|
||||||
|
|
||||||
Sometimes it makes sense to add source code to the error message later.
|
Sometimes it makes sense to add source code to the error message later.
|
||||||
One option is to use [`with_source_code()`](Report::with_source_code)
|
One option is to use [`with_source_code()`](Report::with_source_code)
|
||||||
|
|
@ -535,7 +533,7 @@ fn main() -> miette::Result<()> {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... Diagnostic-based error sources.
|
### ... Diagnostic-based error sources.
|
||||||
|
|
||||||
When one uses the `#[source]` attribute on a field, that usually comes
|
When one uses the `#[source]` attribute on a field, that usually comes
|
||||||
from `thiserror`, and implements a method for
|
from `thiserror`, and implements a method for
|
||||||
|
|
@ -568,7 +566,7 @@ struct MyError {
|
||||||
struct OtherError;
|
struct OtherError;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ... handler options
|
### ... handler options
|
||||||
|
|
||||||
[`MietteHandler`] is the default handler, and is very customizable. In
|
[`MietteHandler`] is the default handler, and is very customizable. In
|
||||||
most cases, you can simply use [`MietteHandlerOpts`] to tweak its behavior
|
most cases, you can simply use [`MietteHandlerOpts`] to tweak its behavior
|
||||||
|
|
@ -587,13 +585,32 @@ miette::set_hook(Box::new(|_| {
|
||||||
.build(),
|
.build(),
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See the docs for [`MietteHandlerOpts`] for more details on what you can
|
See the docs for [`MietteHandlerOpts`] for more details on what you can
|
||||||
customize!
|
customize!
|
||||||
|
|
||||||
### Acknowledgements
|
### ... dynamic diagnostics
|
||||||
|
|
||||||
|
If you...
|
||||||
|
- ...don't know all the possible errors upfront
|
||||||
|
- ...need to serialize/deserialize errors
|
||||||
|
then you may want to use [`miette!`], [`diagnostic!`] macros or
|
||||||
|
[`MietteDiagnostic`] directly to create diagnostic on the fly.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let source = "2 + 2 * 2 = 8".to_string();
|
||||||
|
let report = miette!(
|
||||||
|
labels = vec[
|
||||||
|
LabeledSpan::at(12..13, "this should be 6"),
|
||||||
|
],
|
||||||
|
help = "'*' has greater precedence than '+'",
|
||||||
|
"Wrong answer"
|
||||||
|
).with_source_code(source);
|
||||||
|
println!("{:?}", report)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
`miette` was not developed in a void. It owes enormous credit to various
|
`miette` was not developed in a void. It owes enormous credit to various
|
||||||
other projects and their authors:
|
other projects and their authors:
|
||||||
|
|
@ -612,7 +629,7 @@ other projects and their authors:
|
||||||
- [`ariadne`](https://crates.io/crates/ariadne) for pushing forward how
|
- [`ariadne`](https://crates.io/crates/ariadne) for pushing forward how
|
||||||
_pretty_ these diagnostics can really look!
|
_pretty_ these diagnostics can really look!
|
||||||
|
|
||||||
### License
|
## License
|
||||||
|
|
||||||
`miette` is released to the Rust community under the [Apache license
|
`miette` is released to the Rust community under the [Apache license
|
||||||
2.0](./LICENSE).
|
2.0](./LICENSE).
|
||||||
|
|
@ -623,11 +640,13 @@ under the Apache License. Some code is taken from
|
||||||
[`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed.
|
[`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed.
|
||||||
|
|
||||||
[`miette!`]: https://docs.rs/miette/latest/miette/macro.miette.html
|
[`miette!`]: https://docs.rs/miette/latest/miette/macro.miette.html
|
||||||
|
[`diagnostic!`]: https://docs.rs/miette/latest/miette/macro.diagnostic.html
|
||||||
[`std::error::Error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html
|
[`std::error::Error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html
|
||||||
[`Diagnostic`]: https://docs.rs/miette/latest/miette/trait.Diagnostic.html
|
[`Diagnostic`]: https://docs.rs/miette/latest/miette/trait.Diagnostic.html
|
||||||
[`IntoDiagnostic`]: https://docs.rs/miette/latest/miette/trait.IntoDiagnostic.html
|
[`IntoDiagnostic`]: https://docs.rs/miette/latest/miette/trait.IntoDiagnostic.html
|
||||||
[`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.html
|
[`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.html
|
||||||
[`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
|
[`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
|
||||||
|
[`MietteDiagnostic`]: https://docs.rs/miette/latest/miette/struct.MietteDiagnostic.html
|
||||||
[`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
|
[`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
|
||||||
[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
|
[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
|
||||||
[`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
|
[`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,13 @@
|
||||||
{{readme}}
|
{{readme}}
|
||||||
|
|
||||||
[`miette!`]: https://docs.rs/miette/latest/miette/macro.miette.html
|
[`miette!`]: https://docs.rs/miette/latest/miette/macro.miette.html
|
||||||
|
[`diagnostic!`]: https://docs.rs/miette/latest/miette/macro.diagnostic.html
|
||||||
[`std::error::Error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html
|
[`std::error::Error`]: https://doc.rust-lang.org/nightly/std/error/trait.Error.html
|
||||||
[`Diagnostic`]: https://docs.rs/miette/latest/miette/struct.Diagnostic.html
|
[`Diagnostic`]: https://docs.rs/miette/latest/miette/trait.Diagnostic.html
|
||||||
[`IntoDiagnostic`]: https://docs.rs/miette/latest/miette/trait.IntoDiagnostic.html
|
[`IntoDiagnostic`]: https://docs.rs/miette/latest/miette/trait.IntoDiagnostic.html
|
||||||
[`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.html
|
[`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.html
|
||||||
[`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
|
[`MietteHandler`]: https://docs.rs/miette/latest/miette/struct.MietteHandler.html
|
||||||
|
[`MietteDiagnostic`]: https://docs.rs/miette/latest/miette/struct.MietteDiagnostic.html
|
||||||
[`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
|
[`Report`]: https://docs.rs/miette/latest/miette/struct.Report.html
|
||||||
[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
|
[`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html
|
||||||
[`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
|
[`Result`]: https://docs.rs/miette/latest/miette/type.Result.html
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,14 @@
|
||||||
/// # let resource = 0;
|
/// # let resource = 0;
|
||||||
/// #
|
/// #
|
||||||
/// if !has_permission(user, resource) {
|
/// if !has_permission(user, resource) {
|
||||||
/// bail!("permission denied for accessing {}", resource);
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#" bail!("permission denied for accessing {resource}");"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#" bail!("permission denied for accessing {}", resource);"#
|
||||||
|
)]
|
||||||
/// }
|
/// }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
|
@ -48,17 +55,37 @@
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use miette::{bail, Result, Severity};
|
||||||
|
///
|
||||||
|
/// fn divide(x: f64, y: f64) -> Result<f64> {
|
||||||
|
/// if y.abs() < 1e-3 {
|
||||||
|
/// bail!(
|
||||||
|
/// severity = Severity::Warning,
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#" "dividing by value ({y}) close to 0""#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#" "dividing by value ({}) close to 0", y"#
|
||||||
|
)]
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// Ok(x / y)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! bail {
|
macro_rules! bail {
|
||||||
($msg:literal $(,)?) => {
|
($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
|
||||||
return $crate::private::Err($crate::miette!($msg));
|
return $crate::private::Err(
|
||||||
|
$crate::miette!($($key = $value,)* $fmt $($arg)*)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
($err:expr $(,)?) => {
|
($err:expr $(,)?) => {
|
||||||
return $crate::private::Err($crate::miette!($err));
|
return $crate::private::Err($crate::miette!($err));
|
||||||
};
|
};
|
||||||
($fmt:expr, $($arg:tt)*) => {
|
|
||||||
return $crate::private::Err($crate::miette!($fmt, $($arg)*));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return early with an error if a condition is not satisfied.
|
/// Return early with an error if a condition is not satisfied.
|
||||||
|
|
@ -105,11 +132,33 @@ macro_rules! bail {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use miette::{ensure, Result, Severity};
|
||||||
|
///
|
||||||
|
/// fn divide(x: f64, y: f64) -> Result<f64> {
|
||||||
|
/// ensure!(
|
||||||
|
/// y.abs() >= 1e-3,
|
||||||
|
/// severity = Severity::Warning,
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#" "dividing by value ({y}) close to 0""#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#" "dividing by value ({}) close to 0", y"#
|
||||||
|
)]
|
||||||
|
/// );
|
||||||
|
/// Ok(x / y)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! ensure {
|
macro_rules! ensure {
|
||||||
($cond:expr, $msg:literal $(,)?) => {
|
($cond:expr, $($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
|
||||||
if !$cond {
|
if !$cond {
|
||||||
return $crate::private::Err($crate::miette!($msg));
|
return $crate::private::Err(
|
||||||
|
$crate::miette!($($key = $value,)* $fmt $($arg)*)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($cond:expr, $err:expr $(,)?) => {
|
($cond:expr, $err:expr $(,)?) => {
|
||||||
|
|
@ -117,34 +166,57 @@ macro_rules! ensure {
|
||||||
return $crate::private::Err($crate::miette!($err));
|
return $crate::private::Err($crate::miette!($err));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($cond:expr, $fmt:expr, $($arg:tt)*) => {
|
|
||||||
if !$cond {
|
|
||||||
return $crate::private::Err($crate::miette!($fmt, $($arg)*));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct an ad-hoc error from a string.
|
/// Construct an ad-hoc [`Report`].
|
||||||
///
|
///
|
||||||
/// This evaluates to an `Error`. It can take either just a string, or a format
|
/// # Examples
|
||||||
/// string with arguments. It also can take any custom type which implements
|
|
||||||
/// `Debug` and `Display`.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
///
|
||||||
|
/// With string literal and interpolation:
|
||||||
/// ```
|
/// ```
|
||||||
/// # type V = ();
|
/// # use miette::miette;
|
||||||
/// #
|
/// let x = 1;
|
||||||
/// use miette::{miette, Result};
|
/// let y = 2;
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#"let report = miette!("{x} + {} = {z}", y, z = x + y);"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#"let report = miette!("{} + {} = {z}", x, y, z = x + y);"#
|
||||||
|
)]
|
||||||
///
|
///
|
||||||
/// fn lookup(key: &str) -> Result<V> {
|
/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3");
|
||||||
/// if key.len() != 16 {
|
|
||||||
/// return Err(miette!("key length must be 16 characters, got {:?}", key));
|
|
||||||
/// }
|
|
||||||
///
|
///
|
||||||
/// // ...
|
/// let z = x + y;
|
||||||
/// # Ok(())
|
#[cfg_attr(
|
||||||
/// }
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#"let report = miette!("{x} + {y} = {z}");"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#"let report = miette!("{} + {} = {}", x, y, z);"#
|
||||||
|
)]
|
||||||
|
/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3");
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// With [`diagnostic!`]-like arguments:
|
||||||
|
/// ```
|
||||||
|
/// use miette::{miette, LabeledSpan, Severity};
|
||||||
|
///
|
||||||
|
/// let source = "(2 + 2".to_string();
|
||||||
|
/// let report = miette!(
|
||||||
|
/// // Those fields are optional
|
||||||
|
/// severity = Severity::Error,
|
||||||
|
/// code = "expected::rparen",
|
||||||
|
/// help = "always close your parens",
|
||||||
|
/// labels = vec![LabeledSpan::at_offset(6, "here")],
|
||||||
|
/// url = "https://example.com",
|
||||||
|
/// // Rest of the arguments are passed to `format!`
|
||||||
|
/// // to form diagnostic message
|
||||||
|
/// "expected closing ')'"
|
||||||
|
/// )
|
||||||
|
/// .with_source_code(source);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## `anyhow`/`eyre` Users
|
/// ## `anyhow`/`eyre` Users
|
||||||
|
|
@ -152,17 +224,69 @@ macro_rules! ensure {
|
||||||
/// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`.
|
/// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! miette {
|
macro_rules! miette {
|
||||||
($msg:literal $(,)?) => {
|
($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
|
||||||
// Handle $:literal as a special case to make cargo-expanded code more
|
$crate::Report::from(
|
||||||
// concise in the common case.
|
$crate::diagnostic!($($key = $value,)* $fmt $($arg)*)
|
||||||
$crate::private::new_adhoc($msg)
|
)
|
||||||
};
|
};
|
||||||
($err:expr $(,)?) => ({
|
($err:expr $(,)?) => ({
|
||||||
use $crate::private::kind::*;
|
use $crate::private::kind::*;
|
||||||
let error = $err;
|
let error = $err;
|
||||||
(&error).miette_kind().new(error)
|
(&error).miette_kind().new(error)
|
||||||
});
|
});
|
||||||
($fmt:expr, $($arg:tt)*) => {
|
}
|
||||||
$crate::private::new_adhoc(format!($fmt, $($arg)*))
|
|
||||||
};
|
/// Construct a [`MietteDiagnostic`] in more user-friendly way.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{diagnostic, LabeledSpan, Severity};
|
||||||
|
///
|
||||||
|
/// let source = "(2 + 2".to_string();
|
||||||
|
/// let diag = diagnostic!(
|
||||||
|
/// // Those fields are optional
|
||||||
|
/// severity = Severity::Error,
|
||||||
|
/// code = "expected::rparen",
|
||||||
|
/// help = "always close your parens",
|
||||||
|
/// labels = vec![LabeledSpan::at_offset(6, "here")],
|
||||||
|
/// url = "https://example.com",
|
||||||
|
/// // Rest of the arguments are passed to `format!`
|
||||||
|
/// // to form diagnostic message
|
||||||
|
/// "expected closing ')'",
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
/// Diagnostic without any fields:
|
||||||
|
/// ```
|
||||||
|
/// # use miette::diagnostic;
|
||||||
|
/// let x = 1;
|
||||||
|
/// let y = 2;
|
||||||
|
///
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#" let diag = diagnostic!("{x} + {} = {z}", y, z = x + y);"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#" let diag = diagnostic!("{} + {} = {z}", x, y, z = x + y);"#
|
||||||
|
)]
|
||||||
|
/// assert_eq!(diag.message, "1 + 2 = 3");
|
||||||
|
///
|
||||||
|
/// let z = x + y;
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "no-format-args-capture"),
|
||||||
|
doc = r#"let diag = diagnostic!("{x} + {y} = {z}");"#
|
||||||
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "no-format-args-capture",
|
||||||
|
doc = r#"let diag = diagnostic!("{} + {} = {}", x, y, z);"#
|
||||||
|
)]
|
||||||
|
/// assert_eq!(diag.message, "1 + 2 = 3");
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! diagnostic {
|
||||||
|
($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {{
|
||||||
|
let mut diag = $crate::MietteDiagnostic::new(format!($fmt $($arg)*));
|
||||||
|
$(diag.$key = Some($value.into());)*
|
||||||
|
diag
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
src/lib.rs
27
src/lib.rs
|
|
@ -46,6 +46,7 @@
|
||||||
//! - [... multiple related errors](#-multiple-related-errors)
|
//! - [... multiple related errors](#-multiple-related-errors)
|
||||||
//! - [... delayed source code](#-delayed-source-code)
|
//! - [... delayed source code](#-delayed-source-code)
|
||||||
//! - [... handler options](#-handler-options)
|
//! - [... handler options](#-handler-options)
|
||||||
|
//! - [... dynamic diagnostics](#-dynamic-diagnostics)
|
||||||
//! - [Acknowledgements](#acknowledgements)
|
//! - [Acknowledgements](#acknowledgements)
|
||||||
//! - [License](#license)
|
//! - [License](#license)
|
||||||
//!
|
//!
|
||||||
|
|
@ -249,7 +250,7 @@
|
||||||
//! To construct your own simple adhoc error use the [miette!] macro:
|
//! To construct your own simple adhoc error use the [miette!] macro:
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! // my_app/lib/my_internal_file.rs
|
//! // my_app/lib/my_internal_file.rs
|
||||||
//! use miette::{IntoDiagnostic, Result, WrapErr, miette};
|
//! use miette::{miette, IntoDiagnostic, Result, WrapErr};
|
||||||
//! use semver::Version;
|
//! use semver::Version;
|
||||||
//!
|
//!
|
||||||
//! pub fn some_tool() -> Result<Version> {
|
//! pub fn some_tool() -> Result<Version> {
|
||||||
|
|
@ -590,6 +591,28 @@
|
||||||
//! See the docs for [`MietteHandlerOpts`] for more details on what you can
|
//! See the docs for [`MietteHandlerOpts`] for more details on what you can
|
||||||
//! customize!
|
//! customize!
|
||||||
//!
|
//!
|
||||||
|
//! ### ... dynamic diagnostics
|
||||||
|
//!
|
||||||
|
//! If you...
|
||||||
|
//! - ...don't know all the possible errors upfront
|
||||||
|
//! - ...need to serialize/deserialize errors
|
||||||
|
//! then you may want to use [`miette!`], [`diagnostic!`] macros or
|
||||||
|
//! [`MietteDiagnostic`] directly to create diagnostic on the fly.
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! # use miette::{miette, LabeledSpan, Report};
|
||||||
|
//!
|
||||||
|
//! let source = "2 + 2 * 2 = 8".to_string();
|
||||||
|
//! let report = miette!(
|
||||||
|
//! labels = vec[
|
||||||
|
//! LabeledSpan::at(12..13, "this should be 6"),
|
||||||
|
//! ],
|
||||||
|
//! help = "'*' has greater precedence than '+'",
|
||||||
|
//! "Wrong answer"
|
||||||
|
//! ).with_source_code(source);
|
||||||
|
//! println!("{:?}", report)
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! ## Acknowledgements
|
//! ## Acknowledgements
|
||||||
//!
|
//!
|
||||||
//! `miette` was not developed in a void. It owes enormous credit to various
|
//! `miette` was not developed in a void. It owes enormous credit to various
|
||||||
|
|
@ -624,6 +647,7 @@ pub use eyreish::*;
|
||||||
#[cfg(feature = "fancy-no-backtrace")]
|
#[cfg(feature = "fancy-no-backtrace")]
|
||||||
pub use handler::*;
|
pub use handler::*;
|
||||||
pub use handlers::*;
|
pub use handlers::*;
|
||||||
|
pub use miette_diagnostic::*;
|
||||||
pub use named_source::*;
|
pub use named_source::*;
|
||||||
#[cfg(feature = "fancy")]
|
#[cfg(feature = "fancy")]
|
||||||
pub use panic::*;
|
pub use panic::*;
|
||||||
|
|
@ -638,6 +662,7 @@ mod handler;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod macro_helpers;
|
pub mod macro_helpers;
|
||||||
|
mod miette_diagnostic;
|
||||||
mod named_source;
|
mod named_source;
|
||||||
#[cfg(feature = "fancy")]
|
#[cfg(feature = "fancy")]
|
||||||
mod panic;
|
mod panic;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Diagnostic, LabeledSpan, Severity};
|
||||||
|
|
||||||
|
/// Diagnostic that can be created at runtime.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct MietteDiagnostic {
|
||||||
|
/// Displayed diagnostic message
|
||||||
|
pub message: String,
|
||||||
|
/// Unique diagnostic code to look up more information
|
||||||
|
/// about this Diagnostic. Ideally also globally unique, and documented
|
||||||
|
/// in the toplevel crate's documentation for easy searching.
|
||||||
|
/// Rust path format (`foo::bar::baz`) is recommended, but more classic
|
||||||
|
/// codes like `E0123` will work just fine
|
||||||
|
pub code: Option<String>,
|
||||||
|
/// [`Diagnostic`] severity. Intended to be used by
|
||||||
|
/// [`ReportHandler`](crate::ReportHandler)s to change the way different
|
||||||
|
/// [`Diagnostic`]s are displayed. Defaults to [`Severity::Error`]
|
||||||
|
pub severity: Option<Severity>,
|
||||||
|
/// Additional help text related to this Diagnostic
|
||||||
|
pub help: Option<String>,
|
||||||
|
/// URL to visit for a more detailed explanation/help about this
|
||||||
|
/// [`Diagnostic`].
|
||||||
|
pub url: Option<String>,
|
||||||
|
/// Labels to apply to this `Diagnostic`'s [`Diagnostic::source_code`]
|
||||||
|
pub labels: Option<Vec<LabeledSpan>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MietteDiagnostic {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", &self.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for MietteDiagnostic {}
|
||||||
|
|
||||||
|
impl Diagnostic for MietteDiagnostic {
|
||||||
|
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
self.code
|
||||||
|
.as_ref()
|
||||||
|
.map(Box::new)
|
||||||
|
.map(|c| c as Box<dyn Display>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn severity(&self) -> Option<Severity> {
|
||||||
|
self.severity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
self.help
|
||||||
|
.as_ref()
|
||||||
|
.map(Box::new)
|
||||||
|
.map(|c| c as Box<dyn Display>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
|
||||||
|
self.url
|
||||||
|
.as_ref()
|
||||||
|
.map(Box::new)
|
||||||
|
.map(|c| c as Box<dyn Display>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
|
||||||
|
self.labels
|
||||||
|
.as_ref()
|
||||||
|
.map(|ls| ls.iter().cloned())
|
||||||
|
.map(Box::new)
|
||||||
|
.map(|b| b as Box<dyn Iterator<Item = LabeledSpan>>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MietteDiagnostic {
|
||||||
|
/// Create a new dynamic diagnostic with the given message.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, MietteDiagnostic, Severity};
|
||||||
|
///
|
||||||
|
/// let diag = MietteDiagnostic::new("Oops, something went wrong!");
|
||||||
|
/// assert_eq!(diag.to_string(), "Oops, something went wrong!");
|
||||||
|
/// assert_eq!(diag.message, "Oops, something went wrong!");
|
||||||
|
/// ```
|
||||||
|
pub fn new(message: impl Into<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
message: message.into(),
|
||||||
|
labels: None,
|
||||||
|
severity: None,
|
||||||
|
code: None,
|
||||||
|
help: None,
|
||||||
|
url: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let diag = MietteDiagnostic::new("Oops, something went wrong!").with_code("foo::bar::baz");
|
||||||
|
/// assert_eq!(diag.message, "Oops, something went wrong!");
|
||||||
|
/// assert_eq!(diag.code, Some("foo::bar::baz".to_string()));
|
||||||
|
/// ```
|
||||||
|
pub fn with_code(mut self, code: impl Into<String>) -> Self {
|
||||||
|
self.code = Some(code.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given severity.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, MietteDiagnostic, Severity};
|
||||||
|
///
|
||||||
|
/// let diag = MietteDiagnostic::new("I warn you to stop!").with_severity(Severity::Warning);
|
||||||
|
/// assert_eq!(diag.message, "I warn you to stop!");
|
||||||
|
/// assert_eq!(diag.severity, Some(Severity::Warning));
|
||||||
|
/// ```
|
||||||
|
pub fn with_severity(mut self, severity: Severity) -> Self {
|
||||||
|
self.severity = Some(severity);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given help message.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let diag = MietteDiagnostic::new("PC is not working").with_help("Try to reboot it again");
|
||||||
|
/// assert_eq!(diag.message, "PC is not working");
|
||||||
|
/// assert_eq!(diag.help, Some("Try to reboot it again".to_string()));
|
||||||
|
/// ```
|
||||||
|
pub fn with_help(mut self, help: impl Into<String>) -> Self {
|
||||||
|
self.help = Some(help.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given URL.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let diag = MietteDiagnostic::new("PC is not working")
|
||||||
|
/// .with_url("https://letmegooglethat.com/?q=Why+my+pc+doesn%27t+work");
|
||||||
|
/// assert_eq!(diag.message, "PC is not working");
|
||||||
|
/// assert_eq!(
|
||||||
|
/// diag.url,
|
||||||
|
/// Some("https://letmegooglethat.com/?q=Why+my+pc+doesn%27t+work".to_string())
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn with_url(mut self, url: impl Into<String>) -> Self {
|
||||||
|
self.url = Some(url.into());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given label.
|
||||||
|
///
|
||||||
|
/// Discards previous labels
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let source = "cpp is the best language";
|
||||||
|
///
|
||||||
|
/// let label = LabeledSpan::at(0..3, "This should be Rust");
|
||||||
|
/// let diag = MietteDiagnostic::new("Wrong best language").with_label(label.clone());
|
||||||
|
/// assert_eq!(diag.message, "Wrong best language");
|
||||||
|
/// assert_eq!(diag.labels, Some(vec![label]));
|
||||||
|
/// ```
|
||||||
|
pub fn with_label(mut self, label: impl Into<LabeledSpan>) -> Self {
|
||||||
|
self.labels = Some(vec![label.into()]);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with the given labels.
|
||||||
|
///
|
||||||
|
/// Discards previous labels
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let source = "helo wrld";
|
||||||
|
///
|
||||||
|
/// let labels = vec![
|
||||||
|
/// LabeledSpan::at_offset(3, "add 'l'"),
|
||||||
|
/// LabeledSpan::at_offset(6, "add 'r'"),
|
||||||
|
/// ];
|
||||||
|
/// let diag = MietteDiagnostic::new("Typos in 'hello world'").with_labels(labels.clone());
|
||||||
|
/// assert_eq!(diag.message, "Typos in 'hello world'");
|
||||||
|
/// assert_eq!(diag.labels, Some(labels));
|
||||||
|
/// ```
|
||||||
|
pub fn with_labels(mut self, labels: impl IntoIterator<Item = LabeledSpan>) -> Self {
|
||||||
|
self.labels = Some(labels.into_iter().collect());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with new label added to the existing ones.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let source = "helo wrld";
|
||||||
|
///
|
||||||
|
/// let label1 = LabeledSpan::at_offset(3, "add 'l'");
|
||||||
|
/// let label2 = LabeledSpan::at_offset(6, "add 'r'");
|
||||||
|
/// let diag = MietteDiagnostic::new("Typos in 'hello world'")
|
||||||
|
/// .and_label(label1.clone())
|
||||||
|
/// .and_label(label2.clone());
|
||||||
|
/// assert_eq!(diag.message, "Typos in 'hello world'");
|
||||||
|
/// assert_eq!(diag.labels, Some(vec![label1, label2]));
|
||||||
|
/// ```
|
||||||
|
pub fn and_label(mut self, label: impl Into<LabeledSpan>) -> Self {
|
||||||
|
let mut labels = self.labels.unwrap_or_default();
|
||||||
|
labels.push(label.into());
|
||||||
|
self.labels = Some(labels);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return new diagnostic with new labels added to the existing ones.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic};
|
||||||
|
///
|
||||||
|
/// let source = "helo wrld";
|
||||||
|
///
|
||||||
|
/// let label1 = LabeledSpan::at_offset(3, "add 'l'");
|
||||||
|
/// let label2 = LabeledSpan::at_offset(6, "add 'r'");
|
||||||
|
/// let label3 = LabeledSpan::at_offset(9, "add '!'");
|
||||||
|
/// let diag = MietteDiagnostic::new("Typos in 'hello world!'")
|
||||||
|
/// .and_label(label1.clone())
|
||||||
|
/// .and_labels([label2.clone(), label3.clone()]);
|
||||||
|
/// assert_eq!(diag.message, "Typos in 'hello world!'");
|
||||||
|
/// assert_eq!(diag.labels, Some(vec![label1, label2, label3]));
|
||||||
|
/// ```
|
||||||
|
pub fn and_labels(mut self, labels: impl IntoIterator<Item = LabeledSpan>) -> Self {
|
||||||
|
let mut all_labels = self.labels.unwrap_or_default();
|
||||||
|
all_labels.extend(labels.into_iter());
|
||||||
|
self.labels = Some(all_labels);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -160,7 +160,7 @@ impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Sen
|
||||||
/**
|
/**
|
||||||
[`Diagnostic`] severity. Intended to be used by
|
[`Diagnostic`] severity. Intended to be used by
|
||||||
[`ReportHandler`](crate::ReportHandler)s to change the way different
|
[`ReportHandler`](crate::ReportHandler)s to change the way different
|
||||||
[`Diagnostic`]s are displayed.
|
[`Diagnostic`]s are displayed. Defaults to [`Severity::Error`].
|
||||||
*/
|
*/
|
||||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
|
|
@ -169,9 +169,16 @@ pub enum Severity {
|
||||||
/// Warning. Please take note.
|
/// Warning. Please take note.
|
||||||
Warning,
|
Warning,
|
||||||
/// Critical failure. The program cannot continue.
|
/// Critical failure. The program cannot continue.
|
||||||
|
/// This is the default severity, if you don't specify another one.
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Severity {
|
||||||
|
fn default() -> Self {
|
||||||
|
Severity::Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represents readable source code of some sort.
|
Represents readable source code of some sort.
|
||||||
|
|
||||||
|
|
@ -218,6 +225,54 @@ impl LabeledSpan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes a new label at specified span
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::LabeledSpan;
|
||||||
|
///
|
||||||
|
/// let source = "Cpp is the best";
|
||||||
|
/// let label = LabeledSpan::at(0..3, "should be Rust");
|
||||||
|
/// assert_eq!(
|
||||||
|
/// label,
|
||||||
|
/// LabeledSpan::new(Some("should be Rust".to_string()), 0, 3)
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
pub fn at(span: impl Into<SourceSpan>, label: impl Into<String>) -> Self {
|
||||||
|
Self::new_with_span(Some(label.into()), span)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes a new label that points at a specific offset.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::LabeledSpan;
|
||||||
|
///
|
||||||
|
/// let source = "(2 + 2";
|
||||||
|
/// let label = LabeledSpan::at_offset(4, "expected a closing parenthesis");
|
||||||
|
/// assert_eq!(
|
||||||
|
/// label,
|
||||||
|
/// LabeledSpan::new(Some("expected a closing parenthesis".to_string()), 4, 0)
|
||||||
|
/// )
|
||||||
|
/// ```
|
||||||
|
pub fn at_offset(offset: ByteOffset, label: impl Into<String>) -> Self {
|
||||||
|
Self::new(Some(label.into()), offset, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes a new label without text, that underlines a specific span.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use miette::LabeledSpan;
|
||||||
|
///
|
||||||
|
/// let source = "You have an eror here";
|
||||||
|
/// let label = LabeledSpan::underline(12..16);
|
||||||
|
/// assert_eq!(label, LabeledSpan::new(None, 12, 4))
|
||||||
|
/// ```
|
||||||
|
pub fn underline(span: impl Into<SourceSpan>) -> Self {
|
||||||
|
Self::new_with_span(None, span)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the (optional) label string for this `LabeledSpan`.
|
/// Gets the (optional) label string for this `LabeledSpan`.
|
||||||
pub fn label(&self) -> Option<&str> {
|
pub fn label(&self) -> Option<&str> {
|
||||||
self.label.as_deref()
|
self.label.as_deref()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use miette::{miette, Report};
|
use miette::{miette, Report};
|
||||||
|
|
||||||
fn error() -> Report {
|
fn error() -> Report {
|
||||||
miette!(0).wrap_err(1).wrap_err(2).wrap_err(3)
|
miette!("0").wrap_err(1).wrap_err(2).wrap_err(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ mod drop;
|
||||||
|
|
||||||
use self::common::*;
|
use self::common::*;
|
||||||
use self::drop::{DetectDrop, Flag};
|
use self::drop::{DetectDrop, Flag};
|
||||||
use miette::{Diagnostic, Report};
|
use miette::{Diagnostic, MietteDiagnostic, Report};
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
@ -12,11 +12,19 @@ use std::io;
|
||||||
fn test_downcast() {
|
fn test_downcast() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
bail_literal().unwrap_err().downcast::<&str>().unwrap(),
|
bail_literal()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
bail_fmt().unwrap_err().downcast::<String>().unwrap(),
|
bail_fmt()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
|
|
@ -32,11 +40,19 @@ fn test_downcast() {
|
||||||
fn test_downcast_ref() {
|
fn test_downcast_ref() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
*bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(),
|
bail_literal()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast_ref::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
bail_fmt().unwrap_err().downcast_ref::<String>().unwrap(),
|
bail_fmt()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast_ref::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
|
|
@ -52,11 +68,19 @@ fn test_downcast_ref() {
|
||||||
fn test_downcast_mut() {
|
fn test_downcast_mut() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
*bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(),
|
bail_literal()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast_mut::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
bail_fmt().unwrap_err().downcast_mut::<String>().unwrap(),
|
bail_fmt()
|
||||||
|
.unwrap_err()
|
||||||
|
.downcast_mut::<MietteDiagnostic>()
|
||||||
|
.unwrap()
|
||||||
|
.message,
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"oh no!",
|
"oh no!",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue