diff --git a/Cargo.toml b/Cargo.toml
index f90727a..7fd7e16 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,7 +19,7 @@ atty = "0.2.14"
ci_info = "0.14.2"
[dev-dependencies]
-thiserror = "1.0.26"
+semver = "1.0.4"
[workspace]
members = ["miette-derive"]
diff --git a/README.md b/README.md
index faffa2b..403bc4e 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,10 @@ coder! Error code for One Thousand Lines!
## About
-`miette` is a diagnostic 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!):
+`miette` is a diagnostic library for Rust. It includes a series of
+traits/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!):
-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.
+## Table of Contents
-`miette` also includes a (lightweight) `anyhow`/`eyre`-style
-[DiagnosticReport] type which can be returned from application-internal
-functions to make the `?` experience nicer. It's extra easy to use when using
-[DiagnosticResult]!
+- [About](#about)
+- [Features](#features)
+- [Installing](#installing)
+- [Example](#example)
+- [Usage](#usage)
+ - [... in libraries](#-in-libraries)
+ - [... in application code](#-in-application-code)
+ - [... in `main()`](#-in-main)
+ - [... snippets](#-snippets)
+- [License](#license)
-While the `miette` crate bundles some baseline implementations for [Source]
-and [DiagnosticReportPrinter], 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].
+## Features
+
+- Generic [Diagnostic] protocol, compatible (and dependent on) `std::error::Error`.
+- Unique error codes on every [Diagnostic].
+- Super handy derive macro for defining diagnostic metadata.
+- Lightweight [`anyhow`](https://docs.rs/anyhow)/[`eyre`](https://docs.rs/eyre)-style error wrapper type, [DiagnosticReport],
+ which can be returned from `main`.
+- Generic support for arbitrary [Source]s for snippet data, with default support for `String`s included.
+
+The `miette` crate also comes bundles with a default [DiagnosticReportPrinter] with the following features:
+
+- Fancy graphical [diagnostic output](#about), using ANSI/Unicode text
+- single- and multi-line highlighting support
+- Screen reader/braille support, gated on [`NO_COLOR`](http://no-color.org/), and other heuristics.
+- Fully customizable graphical theming (or overriding the printers entirely).
+- Cause chain printing
## Installing
@@ -48,7 +62,7 @@ Using [`cargo-edit`](https://crates.io/crates/cargo-edit):
$ cargo add miette
```
-## Example and Guide
+## Example
```rust
/*
@@ -82,8 +96,8 @@ Use this DiagnosticResult type (or its expanded version) as the return type
throughout your app (but NOT your libraries! Those should always return concrete
types!).
*/
-use miette::DiagnosticResult as Result;
-fn this_fails() -> Result<()> {
+use miette::DiagnosticResult;
+fn this_fails() -> DiagnosticResult<()> {
// You can use plain strings as a `Source`, or anything that implements
// the one-method `Source` trait.
let src = "source\n text\n here".to_string();
@@ -99,12 +113,12 @@ fn this_fails() -> Result<()> {
}
/*
-Now to get everything printed nicely, just return a Result<(), DiagnosticReport>
+Now to get everything printed nicely, just return a DiagnosticResult<()>
and you're all set!
Note: You can swap out the default reporter for a custom one using `miette::set_reporter()`
*/
-fn pretend_this_is_main() -> Result<()> {
+fn pretend_this_is_main() -> DiagnosticResult<()> {
// kaboom~
this_fails()?;
@@ -131,6 +145,119 @@ diagnostic help: try doing it better next time?
diagnostic error code: oops::my::bad
">
+## Usage
+
+### ... in libraries
+
+`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
+regular [std::error::Error].
+
+We highly recommend using something like [`thiserror`](https://docs.rs/thiserror) to define unique error types and error wrappers for your library.
+
+While `miette` integrates smoothly with `thiserror`, it is _not required_. If
+you don't want to use the [Diagnostic] derive macro, you can implement the
+trait directly, just like with `std::error::Error`.
+
+```rust
+// lib/error.rs
+use thiserror::Error;
+use miette::Diagnostic;
+
+#[derive(Error, Diagnostic, Debug)]
+pub enum MyLibError {
+ #[error(transparent)]
+ #[diagnostic(code(my_lib::io_error))]
+ IoError(#[from] std::io::Error),
+
+ #[error("Oops it blew up")]
+ #[diagnostic(code(my_lib::bad_code))]
+ BadThingHappened,
+}
+```
+
+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
+using something like [eyre](https://docs.rs/eyre) in a library.
+
+### ... in application code
+
+Application code tends to work a little differently than libraries. You don't
+always need or care to define dedicated error wrappers for errors coming from
+external libraries and tools.
+
+For this situation, `miette` includes two tools: [DiagnosticReport] and
+[IntoDiagnostic]. They work in tandem to make it easy to convert regular
+`std::error::Error`s into [Diagnostic]s. Additionally, there's a
+[DiagnosticResult] type alias that you can use to be more terse:
+
+```rust
+// my_app/lib/my_internal_file.rs
+use miette::{IntoDiagnostic, DiagnosticResult};
+use semver::Version;
+
+pub fn some_tool() -> DiagnosticResult {
+ Ok("1.2.x".parse().into_diagnostic("my_app::semver::parse_error")?)
+}
+```
+
+### ... in `main()`
+
+`main()` is just like any other part of your application-internal code. Use
+`DiagnosticResult` as your return value, and it will pretty-print your
+diagnostics automatically.
+
+```rust
+use miette::{DiagnosticResult, IntoDiagnostic};
+use semver::Version;
+
+fn pretend_this_is_main() -> DiagnosticResult<()> {
+ let version: Version = "1.2.x".parse().into_diagnostic("my_app::semver::parse_error")?;
+ println!("{}", version);
+ Ok(())
+}
+```
+
+### ... snippets
+
+Along with its general error handling and reporting features, `miette` also
+includes facilities for adding error spans and annotations/highlights to your
+output. This can be very useful when an error is syntax-related, but you can
+even use it to print out sections of your own source code!
+
+To achieve this, `miette` defines its own lightweight [SourceSpan] type. This
+is a basic byte-offset and length into an associated [Source] and, along with
+the latter, gives `miette` all the information it needs to pretty-print some
+snippets!
+
+The easiest way to define errors like this is to use the `derive(Diagnostic)`
+macro:
+
+```rust
+use miette::{Diagnostic, SourceSpan};
+use thiserror::Error;
+
+#[derive(Diagnostic, Debug, Error)]
+#[error("oops")]
+#[diagnostic(code(my_lib::random_error))]
+pub struct MyErrorType {
+ // The `Source` that miette will use.
+ src: String,
+
+ // A snippet that points to `src`, our `Source`. The filename can be
+ // provided at the callsite.
+ #[snippet(src, "This is the snippet")]
+ snip: SourceSpan,
+
+ // A highlight for the `snip` snippet we defined above. This will
+ // underline/mark the specific code inside the larger snippet context.
+ //
+ // The label is provided using `SourceSpan`'s label.
+ #[highlight(snip)]
+ err_span: SourceSpan,
+}
+```
+
## License
`miette` is released to the Rust community under the [Apache license 2.0](./LICENSE).
diff --git a/src/lib.rs b/src/lib.rs
index 1c18995..aed062d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,7 @@
#![doc = include_str!("../README.md")]
+#![deny(missing_docs)]
+// #![deny(missing_docs, missing_debug_implementations, nonstandard_style)]
+// #![warn(unreachable_pub, rust_2018_idioms)]
pub use miette_derive::*;
diff --git a/src/printer/narratable_printer.rs b/src/printer/narratable_printer.rs
index 6e99f3f..002ff88 100644
--- a/src/printer/narratable_printer.rs
+++ b/src/printer/narratable_printer.rs
@@ -5,15 +5,16 @@ use crate::protocol::{Diagnostic, DiagnosticReportPrinter, DiagnosticSnippet, Se
use crate::{SourceSpan, SpanContents};
/**
-Reference implementation of the [DiagnosticReportPrinter] trait. This is generally
-good enough for simple use-cases, and is the default one installed with `miette`,
-but you might want to implement your own if you want custom reporting for your
-tool or app.
+[DiagnosticReportPrinter] that renders plain text and avoids extraneous graphics.
+It's optimized for screen readers and braille users, but is also used in any
+non-graphical environments, such as non-TTY output.
*/
#[derive(Debug, Clone)]
pub struct NarratableReportPrinter;
impl NarratableReportPrinter {
+ /// Create a new [NarratableReportPrinter]. There are no customization
+ /// options.
pub fn new() -> Self {
Self
}
@@ -26,6 +27,10 @@ impl Default for NarratableReportPrinter {
}
impl NarratableReportPrinter {
+ /// Render a [Diagnostic]. This function is mostly internal and meant to
+ /// be called by the toplevel [DiagnosticReportPrinter] handler, but is
+ /// made public to make it easier (possible) to test in isolation from
+ /// global state.
pub fn render_report(
&self,
f: &mut impl fmt::Write,
diff --git a/src/protocol.rs b/src/protocol.rs
index 38b8ec9..7a33cf7 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -233,6 +233,7 @@ pub struct SourceSpan {
}
impl SourceSpan {
+ /// Create a new [SourceSpan].
pub fn new(start: SourceOffset, length: SourceOffset) -> Self {
Self {
label: None,
@@ -241,6 +242,7 @@ impl SourceSpan {
}
}
+ /// Create a new [SourceSpan] with a label.
pub fn new_labeled(label: impl AsRef, start: SourceOffset, length: SourceOffset) -> Self {
Self {
label: Some(label.as_ref().into()),
@@ -249,18 +251,27 @@ impl SourceSpan {
}
}
+ /// The absolute offset, in bytes, from the beginning of a [Source].
pub fn offset(&self) -> usize {
self.offset.offset()
}
+ /// Returns a reference to this [SourceSpan]'s label. This label may be
+ /// used for different things in different contexts. In highlights, it
+ /// will be interpreted as the text on the other end of an underscored
+ /// section of text. In snippet spans, this will be treated as the source
+ /// name.
pub fn label(&self) -> Option<&str> {
self.label.as_ref().map(|x| &x[..])
}
+ /// Total length of the [SourceSpan], in bytes.
pub fn len(&self) -> usize {
self.length.offset()
}
+ /// Whether this [SourceSpan] has a length of zero. It may still be useful
+ /// to point to a specific point.
pub fn is_empty(&self) -> bool {
self.length.offset() == 0
}