3 releases (breaking)
0.2.0 | Nov 21, 2024 |
---|---|
0.1.0 | Sep 12, 2023 |
0.0.0 | Jul 21, 2022 |
#405 in Rust patterns
3,902 downloads per month
Used in 24 crates
(via wax)
35KB
592 lines
Tardar is a Rust library that provides extensions for the miette
crate.
Diagnostic Result
s are the primary extension, which pair an output with
accumulated Diagnostic
s for both success and failure (the Ok
and Err
variants).
lib.rs
:
Tardar is a Rust library that provides extensions for the miette
crate. These
extensions primarily provide more ergonomic diagnostic Result
s and collation of
Diagnostic
s.
Diagnostic Results
DiagnosticResult
is a Result
type that accumulates and associates Diagnostic
s with
an output type T
for both success and failure (Ok
and Err
variants). The Ok
variant
contains a Diagnosed
with a T
and zero or more non-error Diagnostic
s. The Err
variant contains an Error
with one or more Diagnostic
s, at least one of which is
considered an error.
Together with extension methods, DiagnosticResult
supports fluent and ergonomic composition
of diagnostic functions. Here, a diagnostic function is one that returns a
DiagnosticResult
or other container of Diagnostic
s. For example, a library that parses
a data structure or language from text may use diagnostic functions for parsing and analysis.
use tardar::DiagnosticResult;
#
/// Parses an expression into an abstract syntax tree (AST).
fn parse(expression: &str) -> DiagnosticResult<Ast<'_>> {
...
}
/// Checks an AST for token, syntax, and rule correctness.
fn check<'x>(tree: Ast<'x>) -> DiagnosticResult<Checked<Ast<'x>>> {
...
}
These diagnostic functions can be composed with extension methods.
use tardar::prelude::*;
use tardar::DiagnosticResult;
#
#
#
/// Parses an expression into a checked AST.
pub fn parse_and_check(expression: &str) -> DiagnosticResult<Checked<Ast<'_>>> {
parse(expression).and_then_diagnose(check)
}
The parse_and_check
function forwards the output of parse
to check
with
and_then_diagnose
. This function is much like the
standard Result::and_then
, but accepts a diagnostic function and so preserves any input
Diagnostic
s. If parse
succeeds with some warnings but check
fails with an error, then
the output Error
will contain both the warning and error Diagnostic
s.
Other shapes of diagnostic functions can also be composed. For example, an analysis function may accept a shared reference and return an iterator rather than a result, since it cannot conceptually fail.
use tardar::BoxedDiagnostic;
#
/// Analyzes a checked AST and returns non-error diagnostics.
fn analyze<'x>(tree: &Checked<Ast<'x>>) -> impl Iterator<Item = BoxedDiagnostic> {
...
}
This diagnostic function can too be composed into parse_and_check
using extension methods.
use tardar::prelude::*;
use tardar::{BoxedDiagnostic, DiagnosticResult};
#
#
#
#
/// Parses an expression into a checked AST with analysis.
pub fn parse_and_check(expression: &str) -> DiagnosticResult<Checked<Ast<'_>>> {
parse(expression)
.and_then_diagnose(check)
.diagnose_non_errors(analyze)
}
The output of check
is forwarded to analyze
with
diagnose_non_errors
. This function is more bespoke
and accepts a diagnostic function that itself accepts the output T
by shared reference. Any
Diagnostic
s returned by the accepted function are interpreted as non-errors and are
accumulated into the Diagnosed
.
DiagnosticResult
s can be constructed from related types, such as singular Result
types
and iterators with Diagnostic
items. When extension functions like and_then_diagnose
are
not immediately compatible, it is often possible to perform conversions in a closure.
Collation
miette
primarily groups Diagnostic
s via Diagnostic::related
. However, it can be
inflexible or cumbersome to provide such an implementation and Diagnostic
s are commonly and
more easily organized into collections or iterators. Collation
is a Diagnostic
type
that relates arbitrary non-empty vectors and slices of
Diagnostic
s.
use tardar::{Diagnosed, DiagnosticResult, OwnedCollation};
#
/// Performs an active scan for the given BSSID.
pub fn scan(
client: &Client,
bssid: Bssid,
) -> Result<ActiveScan, OwnedCollation> {
let result: DiagnosticResult<ActiveScan> = {
...
};
// The try operator `?` can be used here, because `Error` can be converted into
// `Collation`. If the result is `Err`, then the `Collation` relates the error diagnostics.
let scan = result.map(Diagnosed::into_output)?;
...
}
Note that DiagnosticResult
s accumulate Diagnostic
s, but do not relate them by
design: neither Diagnosed
nor Error
implement Diagnostic
.
Dependencies
~2.4–3.5MB
~61K SLoC