mirror of https://github.com/zkat/miette.git
docs: rewrite readme
This commit is contained in:
parent
2fb9f93cbf
commit
e1ef944689
207
README.md
207
README.md
|
|
@ -1,73 +1,166 @@
|
||||||
you FAIL miette? you fail her compiler like the unsafe C program? oh! oh! jail for mother! jail for mother for One Thousand Years!!!!
|
# miette
|
||||||
|
|
||||||
|
you FAIL miette? you fail her compiler like the unsafe C program? oh! oh! jail
|
||||||
|
for mother! jail for mother for One Thousand Years!!!!
|
||||||
|
|
||||||
(I'm sorry, I'll come up with a better pun later.)
|
(I'm sorry, I'll come up with a better pun later.)
|
||||||
|
|
||||||
## Examples
|
## About
|
||||||
|
|
||||||
Here's an example of using something like `thisdiagnostic` to define Diagnostics declaratively.
|
`miette` is a diagnostic definition library for Rust. It includes a series of
|
||||||
|
protocols that allow you to hook into its error reporting facilities, and even
|
||||||
|
write your own error reports! It lets you define error types that can print out
|
||||||
|
like this (or in any format you like!):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Error: Error[oops::my::bad]: oops it broke!
|
||||||
|
|
||||||
|
[bad_file.rs] This is the part that broke:
|
||||||
|
|
||||||
|
1 | source
|
||||||
|
2 | text
|
||||||
|
⫶ | ^^^^ this bit here
|
||||||
|
3 | here
|
||||||
|
|
||||||
|
﹦try doing it better next time?
|
||||||
|
```
|
||||||
|
|
||||||
|
The [Diagnostic] trait in `miette` is an extension of `std::error::Error` that
|
||||||
|
adds various facilities like [Severity], error codes that could be looked up
|
||||||
|
by users, and snippet display with support for multiline reports, arbitrary
|
||||||
|
[Source]s, and pretty printing.
|
||||||
|
|
||||||
|
While the `miette` crate bundles some baseline implementations for [Source]
|
||||||
|
and [DiagnosticReporter], it's intended to define a protocol that other crates
|
||||||
|
can build on top of to provide rich error reporting, and encourage an
|
||||||
|
ecosystem that leans on this extra metadata to provide it for others in a way
|
||||||
|
that's compatible with [std::error::Error]
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
Using [`cargo-edit`](https://crates.io/crates/cargo-edit):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ cargo add miette
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example and Guide
|
||||||
|
|
||||||
|
```rust
|
||||||
|
/*
|
||||||
|
First, you implement a regular `std::error::Error` type.
|
||||||
|
|
||||||
|
`thiserror` is a great way to do so, and plays extremely nicely with `miette`!
|
||||||
|
*/
|
||||||
|
|
||||||
```ignore
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use thisdiagnostic::Diagnostic;
|
|
||||||
|
|
||||||
#[derive(Error, Diagnostic)]
|
#[derive(Error)]
|
||||||
pub enum MyDiagnostic {
|
#[error("oops it broke!")]
|
||||||
/// Generally happens because you did some specific thing wrong. The
|
struct MyBad {
|
||||||
/// reason is actually something like <...>
|
snippets: Vec<DiagnosticSnippet>,
|
||||||
#[diagnostic(
|
}
|
||||||
code = fatal::error, // Translates to `mycrate::fatal::error` and creates
|
|
||||||
// a crate-wide alias so you can just search for that
|
|
||||||
// exact full string in rustdoc.
|
|
||||||
help = "Please consider doing things differently next time.",
|
|
||||||
backtrace = true, // Enable showing a backtrace when this is printed.
|
|
||||||
)]
|
|
||||||
#[error("Oopsie poopsie it all exploded.")]
|
|
||||||
FatalError,
|
|
||||||
|
|
||||||
/// This one usually resolves on its own, don't worry about it.
|
/*
|
||||||
#[diagnostic(
|
Next, we have to implement the `Diagnostic` trait for it:
|
||||||
code = nice::warning,
|
*/
|
||||||
help = "This is mostly to help you!",
|
|
||||||
severity = warning
|
|
||||||
)]
|
|
||||||
#[error("This might break in the future.")]
|
|
||||||
NiceWarning,
|
|
||||||
|
|
||||||
/// This diagnostic includes code spans!
|
use miette::{Diagnostic, Severity, DiagnosticSnippet};
|
||||||
#[diagnostic(
|
|
||||||
code = math::bad_arithmetic,
|
|
||||||
help = "Convert {bad_var} into a {good_type} and try again."
|
|
||||||
)]
|
|
||||||
#[error("Tried to add a {bad_type} to a {good_type}")]
|
|
||||||
BadArithmetic {
|
|
||||||
// Regular error metadata for programmatic use.
|
|
||||||
good_type: Type,
|
|
||||||
bad_type: Type,
|
|
||||||
bad_var: Var,
|
|
||||||
|
|
||||||
// Anything implementing the Source trait can be used as a source.
|
impl Diagnostic for MyBad {
|
||||||
src: PathBuf,
|
fn code(&self) -> &(dyn std::fmt::Display + 'static) {
|
||||||
other_src: String,
|
&"oops::my::bad"
|
||||||
|
}
|
||||||
|
|
||||||
// The context is the span of code that will be rendered.
|
fn severity(&self) -> Severity {
|
||||||
// There can be multiple contexts in a Diagnostic.
|
Severity::Error
|
||||||
#[context(src, "This region is where things went wrong.")]
|
}
|
||||||
ctx: SourceSpan,
|
|
||||||
|
|
||||||
// Highlights underline and label specific subsections of the context.
|
fn help(&self) -> Option<&[&str]> {
|
||||||
#[highlight(ctx, "This is a {bad_type}")]
|
Some(&["try doing it better next time?"])
|
||||||
bad_var_span: SourceSpan, // These can span multiple lines!
|
}
|
||||||
|
|
||||||
// They can be optional!
|
fn snippets(&self) -> Option<&[DiagnosticSnippet]> {
|
||||||
#[highlight(ctx, "This is a {good_type}")]
|
Some(&self.snippets)
|
||||||
good_var_span: Option<SourceSpan>,
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
#[error(transparent)]
|
/*
|
||||||
#[diagnostic(transparent)]
|
Then, we implement `std::fmt::Debug` using the included `MietteReporter`,
|
||||||
// renders as "some_library::<code_subpath>". No docs needed.
|
which is able to pretty print diagnostics reasonably well.
|
||||||
// You must re-export `SomeLibraryError` from your crate if you want
|
|
||||||
// users to be able to find its error codes on your own rustdoc search box.
|
You can use any reporter you want here, or no reporter at all,
|
||||||
SomeLibraryError(#[from] some_library::SomeLibraryError)
|
but `Debug` is required by `std::error::Error`, so you need to at
|
||||||
|
least derive it.
|
||||||
|
|
||||||
|
Make sure you pull in the `miette::DiagnosticReporter` trait!.
|
||||||
|
*/
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use miette::{DiagnosticReporter, MietteReporter};
|
||||||
|
|
||||||
|
impl fmt::Debug for MyBad {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
MietteReporter.debug(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now we can use `miette`~
|
||||||
|
*/
|
||||||
|
use miette::{MietteError, SourceSpan};
|
||||||
|
|
||||||
|
fn make_my_error() -> MyBad {
|
||||||
|
// You can use plain strings as a `Source`, bu the protocol is fully extensible!
|
||||||
|
let src = "source\n text\n here".to_string();
|
||||||
|
|
||||||
|
// The Rust runtime will use `{:?}` (Debug) to print any error you return
|
||||||
|
// from `main`!
|
||||||
|
MyBad {
|
||||||
|
// Snippets are **fully optional**, but in some use cases can provide
|
||||||
|
// additional contextual detail for users!
|
||||||
|
//
|
||||||
|
// This is all you need to write to get `rustc`-style, rich error reports!
|
||||||
|
//
|
||||||
|
// See the docs for `DiagnosticSnippet` to learn more about how to
|
||||||
|
// construct these objects!
|
||||||
|
snippets: vec![DiagnosticSnippet {
|
||||||
|
message: Some("This is the part that broke".into()),
|
||||||
|
source_name: "bad_file.rs".into(),
|
||||||
|
source: Box::new(src.clone()),
|
||||||
|
context: SourceSpan {
|
||||||
|
start: 0.into(),
|
||||||
|
end: (src.len() - 1).into(),
|
||||||
|
},
|
||||||
|
highlights: Some(vec![
|
||||||
|
("this bit here".into(), SourceSpan {
|
||||||
|
start: 9.into(),
|
||||||
|
end: 12.into(),
|
||||||
|
})
|
||||||
|
]),
|
||||||
|
}],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
And this is the output you'll get if you run this program:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
Error: Error[oops::my::bad]: oops it broke!
|
||||||
|
|
||||||
|
[bad_file.rs] This is the part that broke:
|
||||||
|
|
||||||
|
1 | source
|
||||||
|
2 | text
|
||||||
|
⫶ | ^^^^ this bit here
|
||||||
|
3 | here
|
||||||
|
|
||||||
|
﹦try doing it better next time?
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
`miette` is released to the Rust community under the [MIT license](./LICENSE).
|
||||||
|
|
||||||
|
It also includes some code taken from [`eyre`](https://github.com/yaahc/eyre),
|
||||||
|
also [under the MIT license](https://github.com/yaahc/eyre#license).
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use thiserror::Error;
|
||||||
#[derive(Error)]
|
#[derive(Error)]
|
||||||
#[error("oops!")]
|
#[error("oops!")]
|
||||||
struct MyBad {
|
struct MyBad {
|
||||||
details: Vec<DiagnosticSnippet>,
|
snippets: Vec<DiagnosticSnippet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for MyBad {
|
impl fmt::Debug for MyBad {
|
||||||
|
|
@ -31,14 +31,14 @@ impl Diagnostic for MyBad {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snippets(&self) -> Option<&[DiagnosticSnippet]> {
|
fn snippets(&self) -> Option<&[DiagnosticSnippet]> {
|
||||||
Some(&self.details)
|
Some(&self.snippets)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic() -> Result<(), MietteError> {
|
fn basic() -> Result<(), MietteError> {
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
details: Vec::new(),
|
snippets: Vec::new(),
|
||||||
};
|
};
|
||||||
let out = format!("{:?}", err);
|
let out = format!("{:?}", err);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -52,7 +52,7 @@ fn basic() -> Result<(), MietteError> {
|
||||||
fn fancy() -> Result<(), MietteError> {
|
fn fancy() -> Result<(), MietteError> {
|
||||||
let src = "source\n text\n here".to_string();
|
let src = "source\n text\n here".to_string();
|
||||||
let err = MyBad {
|
let err = MyBad {
|
||||||
details: vec![DiagnosticSnippet {
|
snippets: vec![DiagnosticSnippet {
|
||||||
message: Some("This is the part that broke".into()),
|
message: Some("This is the part that broke".into()),
|
||||||
source_name: "bad_file.rs".into(),
|
source_name: "bad_file.rs".into(),
|
||||||
source: Box::new(src.clone()),
|
source: Box::new(src.clone()),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue