feat(snippets): Overhauled how snippets handle labels, sources, and messages, including the derive macro

BREAKING CHANGE: this will probably break a lot of your stuff. hopefully rustc helps
This commit is contained in:
Kat Marchán 2021-08-22 19:06:26 -07:00
parent 2d886c06a3
commit 61283e9efe
No known key found for this signature in database
GPG Key ID: AEB529C08A3C7E9E
11 changed files with 392 additions and 347 deletions

View File

@ -30,7 +30,7 @@ diagnostic error code: ruget::api::bad_json
- [Features](#features) - [Features](#features)
- [Installing](#installing) - [Installing](#installing)
- [Example](#example) - [Example](#example)
- [Usage](#usage) - [Using](#using)
- [... in libraries](#-in-libraries) - [... in libraries](#-in-libraries)
- [... in application code](#-in-application-code) - [... in application code](#-in-application-code)
- [... in `main()`](#-in-main) - [... in `main()`](#-in-main)
@ -85,11 +85,12 @@ use thiserror::Error;
)] )]
struct MyBad { struct MyBad {
// The Source that we're gonna be printing snippets out of. // The Source that we're gonna be printing snippets out of.
src: String, // This can be a String if you don't have or care about file names.
src: NamedSource,
// Snippets and highlights can be included in the diagnostic! // Snippets and highlights can be included in the diagnostic!
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
snip: SourceSpan, snip: SourceSpan,
#[highlight(snip)] #[highlight(snip, label("This bit here"))]
bad_bit: SourceSpan, bad_bit: SourceSpan,
} }
@ -100,7 +101,7 @@ Use this DiagnosticResult type (or its expanded version) as the return type
throughout your app (but NOT your libraries! Those should always return concrete throughout your app (but NOT your libraries! Those should always return concrete
types!). types!).
*/ */
use miette::DiagnosticResult; use miette::{DiagnosticResult, NamedSource};
fn this_fails() -> DiagnosticResult<()> { fn this_fails() -> DiagnosticResult<()> {
// You can use plain strings as a `Source`, or anything that implements // You can use plain strings as a `Source`, or anything that implements
// the one-method `Source` trait. // the one-method `Source` trait.
@ -108,9 +109,9 @@ fn this_fails() -> DiagnosticResult<()> {
let len = src.len(); let len = src.len();
Err(MyBad { Err(MyBad {
src, src: NamedSource::new("bad_file.rs", src),
snip: ("bad_file.rs", 0, len).into(), snip: (0, len).into(),
bad_bit: ("this bit here", 9, 4).into(), bad_bit: (9, 4).into(),
})?; })?;
Ok(()) Ok(())
@ -149,7 +150,7 @@ diagnostic help: try doing it better next time?
diagnostic error code: oops::my::bad diagnostic error code: oops::my::bad
"> ">
## Usage ## Using
### ... in libraries ### ... in libraries
@ -244,6 +245,7 @@ use miette::Diagnostic;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Diagnostic, Debug)] #[derive(Error, Diagnostic, Debug)]
#[error("kaboom")]
#[diagnostic( #[diagnostic(
code(my_app::my_error), code(my_app::my_error),
// You can do formatting! // You can do formatting!
@ -268,6 +270,7 @@ use thiserror::Error;
// Will link users to https://docs.rs/my_crate/0.0.0/my_crate/struct.MyErr.html // Will link users to https://docs.rs/my_crate/0.0.0/my_crate/struct.MyErr.html
url(docsrs) url(docsrs)
)] )]
#[error("kaboom")]
struct MyErr; struct MyErr;
``` ```
@ -297,17 +300,25 @@ pub struct MyErrorType {
// The `Source` that miette will use. // The `Source` that miette will use.
src: String, src: String,
// A snippet that points to `src`, our `Source`. The filename can be // A snippet that points to `src`, our `Source`.
// provided at the callsite. #[snippet(
#[snippet(src, "This is the snippet")] src,
message("This is the snippet")
)]
snip: SourceSpan, snip: SourceSpan,
// A highlight for the `snip` snippet we defined above. This will // A highlight for the `snip` snippet we defined above. This will
// underline/mark the specific code inside the larger snippet context. // underline/mark the specific code inside the larger snippet context.
// #[highlight(snip, label("This is the highlight"))]
// The label is provided using `SourceSpan`'s label.
#[highlight(snip)]
err_span: SourceSpan, err_span: SourceSpan,
// You can add as many snippets as you want against the same Source.
// They'll be rendered sequentially.
#[snippet(
src,
message("This is a warning")
)]
snip2: SourceSpan,
} }
``` ```

View File

@ -166,7 +166,7 @@ impl Diagnostic {
let code_body = code.gen_struct(); let code_body = code.gen_struct();
let help_body = help.as_ref().and_then(|x| x.gen_struct(fields)); let help_body = help.as_ref().and_then(|x| x.gen_struct(fields));
let sev_body = severity.as_ref().and_then(|x| x.gen_struct()); let sev_body = severity.as_ref().and_then(|x| x.gen_struct());
let snip_body = snippets.as_ref().and_then(|x| x.gen_struct()); let snip_body = snippets.as_ref().and_then(|x| x.gen_struct(fields));
let url_body = url.as_ref().and_then(|x| x.gen_struct(ident, fields)); let url_body = url.as_ref().and_then(|x| x.gen_struct(ident, fields));
quote! { quote! {

View File

@ -1,72 +1,136 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{ use syn::{
parenthesized,
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned, spanned::Spanned,
Token, Token,
}; };
use crate::diagnostic::DiagnosticVariant; use crate::fmt;
use crate::utils::MemberOrString; use crate::{diagnostic::DiagnosticVariant, fmt::Display};
pub struct Snippets(Vec<Snippet>); pub struct Snippets(Vec<Snippet>);
struct Snippet { struct Snippet {
message: Option<MemberOrString>, message: Option<Display>,
highlights: Vec<Highlight>, highlights: Vec<Highlight>,
source: syn::Member, source: syn::Member,
snippet: syn::Member, snippet: syn::Member,
} }
struct Highlight { struct Highlight {
label: Option<Display>,
highlight: syn::Member, highlight: syn::Member,
} }
struct SnippetAttr { struct SnippetAttr {
source: syn::Member, source: syn::Member,
message: Option<MemberOrString>, message: Option<Display>,
} }
struct HighlightAttr { struct HighlightAttr {
label: Option<Display>,
snippet: syn::Member, snippet: syn::Member,
} }
impl Parse for SnippetAttr { impl Parse for SnippetAttr {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let punc = Punctuated::<MemberOrString, Token![,]>::parse_terminated(input)?; let source = input.parse::<syn::Member>()?;
let span = input.span(); let message = if input.peek(Token![,]) {
let mut iter = punc.into_iter(); input.parse::<Token![,]>()?;
let source = match iter.next() { let ident = input.parse::<syn::Ident>()?;
Some(MemberOrString::Member(member)) => member, if ident == "message" {
_ => { let la = input.lookahead1();
if la.peek(syn::token::Paren) {
let content;
parenthesized!(content in input);
if content.peek(syn::LitStr) {
let fmt = content.parse()?;
let args = if content.is_empty() {
TokenStream::new()
} else {
content.parse::<Token![,]>()?;
fmt::parse_token_expr(&content, false)?
};
let display = Display {
fmt,
args,
has_bonus_display: false,
};
Some(display)
} else {
return Err(syn::Error::new(ident.span(), "Invalid argument to message() sub-attribute. The first argument must be a literal string."));
}
} else {
input.parse::<Token![=]>()?;
Some(Display {
fmt: input.parse()?,
args: TokenStream::new(),
has_bonus_display: false,
})
}
} else {
return Err(syn::Error::new( return Err(syn::Error::new(
span, ident.span(),
"Source must be an identifier that refers to a Source for this snippet.", "Invalid sub-attribute. Only `message()` is allowed.",
)) ));
} }
} else {
None
}; };
let message = iter.next();
Ok(SnippetAttr { source, message }) Ok(SnippetAttr { source, message })
} }
} }
impl Parse for HighlightAttr { impl Parse for HighlightAttr {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
let punc = Punctuated::<MemberOrString, Token![,]>::parse_terminated(input)?; let snippet = input.parse::<syn::Member>()?;
let span = input.span(); let label = if input.peek(Token![,]) {
let mut iter = punc.into_iter(); input.parse::<Token![,]>()?;
let snippet = let ident = input.parse::<syn::Ident>()?;
match iter.next() { if ident == "label" {
Some(MemberOrString::Member(member)) => member, let la = input.lookahead1();
_ => return Err(syn::Error::new( if la.peek(syn::token::Paren) {
span, let content;
"must be an identifier that refers to something with a #[snippet] attribute.", parenthesized!(content in input);
)), if content.peek(syn::LitStr) {
}; let fmt = content.parse()?;
Ok(HighlightAttr { snippet }) let args = if content.is_empty() {
TokenStream::new()
} else {
content.parse::<Token![,]>()?;
fmt::parse_token_expr(&content, false)?
};
let display = Display {
fmt,
args,
has_bonus_display: false,
};
Some(display)
} else {
return Err(syn::Error::new(ident.span(), "Invalid argument to label() sub-attribute. The first argument must be a literal string."));
}
} else {
input.parse::<Token![=]>()?;
Some(Display {
fmt: input.parse()?,
args: TokenStream::new(),
has_bonus_display: false,
})
}
} else {
return Err(syn::Error::new(
ident.span(),
"Invalid sub-attribute. Only `label()` is allowed.",
));
}
} else {
None
};
Ok(HighlightAttr { snippet, label })
} }
} }
@ -113,7 +177,7 @@ impl Snippets {
for (i, field) in fields.iter().enumerate() { for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs { for attr in &field.attrs {
if attr.path.is_ident("highlight") { if attr.path.is_ident("highlight") {
let HighlightAttr { snippet } = attr.parse_args::<HighlightAttr>()?; let HighlightAttr { snippet, label } = attr.parse_args::<HighlightAttr>()?;
if let Some(snippet) = snippets.get_mut(&snippet) { if let Some(snippet) = snippets.get_mut(&snippet) {
let member = if let Some(ident) = field.ident.clone() { let member = if let Some(ident) = field.ident.clone() {
syn::Member::Named(ident) syn::Member::Named(ident)
@ -123,7 +187,10 @@ impl Snippets {
span: field.span(), span: field.span(),
}) })
}; };
snippet.highlights.push(Highlight { highlight: member }); snippet.highlights.push(Highlight {
highlight: member,
label,
});
} else { } else {
return Err(syn::Error::new(snippet.span(), "Highlight must refer to an existing field with a #[snippet(...)] attribute.")); return Err(syn::Error::new(snippet.span(), "Highlight must refer to an existing field with a #[snippet(...)] attribute."));
} }
@ -137,29 +204,35 @@ impl Snippets {
} }
} }
pub(crate) fn gen_struct(&self) -> Option<TokenStream> { pub(crate) fn gen_struct(&self, fields: &syn::Fields) -> Option<TokenStream> {
let snippets = self.0.iter().map(|snippet| { let snippets = self.0.iter().map(|snippet| {
// snippet message // snippet message
let msg = snippet let msg = if let Some(display) = &snippet.message {
.message let members: HashSet<syn::Member> = fields
.as_ref() .iter()
.map(|msg| match msg { .enumerate()
MemberOrString::String(str) => { .map(|(i, field)| {
quote! { if let Some(ident) = field.ident.as_ref().cloned() {
message: std::option::Option::Some(#str), syn::Member::Named(ident)
} else {
syn::Member::Unnamed(syn::Index {
index: i as u32,
span: field.span(),
})
} }
} })
MemberOrString::Member(m) => { .collect();
quote! { let mut display = display.clone();
message: std::option::Option::Some(self.#m.as_ref()), display.expand_shorthand(&members);
} let Display { fmt, args, .. } = display;
} quote! {
}) message: std::option::Option::Some(format!(#fmt, #args)),
.unwrap_or_else(|| { }
quote! { } else {
message: std::option::Option::None, quote! {
} message: std::option::Option::None,
}); }
};
// Source field // Source field
let src_ident = &snippet.source; let src_ident = &snippet.source;
@ -175,9 +248,20 @@ impl Snippets {
// Highlights // Highlights
let highlights = snippet.highlights.iter().map(|highlight| { let highlights = snippet.highlights.iter().map(|highlight| {
let Highlight { highlight } = highlight; let Highlight { highlight, label } = highlight;
quote! { if let Some(Display { fmt, args, .. }) = label {
self.#highlight.clone().into() quote! {
(
std::option::Option::Some(
format!(#fmt, #args)
),
self.#highlight.clone().into()
)
}
} else {
quote! {
(std::option::Option::None, self.#highlight.clone().into())
}
} }
}); });
let highlights = quote! { let highlights = quote! {
@ -211,33 +295,25 @@ impl Snippets {
variant.snippets.as_ref().map(|snippets| { variant.snippets.as_ref().map(|snippets| {
let variant_snippets = snippets.0.iter().map(|snippet| { let variant_snippets = snippets.0.iter().map(|snippet| {
// snippet message // snippet message
let msg = snippet let msg = if let Some(display) = &snippet.message {
.message let members: HashSet<syn::Member> = variant.fields.iter().enumerate().map(|(i, field)| {
.as_ref() if let Some(ident) = field.ident.as_ref().cloned() {
.map(|msg| match msg { syn::Member::Named(ident)
MemberOrString::String(str) => { } else {
quote! { syn::Member::Unnamed(syn::Index { index: i as u32, span: field.span() })
message: std::option::Option::Some(#str), }
} }).collect();
} let mut display = display.clone();
MemberOrString::Member(m) => { display.expand_shorthand(&members);
let m = match m { let Display { fmt, args, .. } = display;
syn::Member::Named(id) => id.clone(), quote! {
syn::Member::Unnamed(syn::Index { index, .. }) => { message: std::option::Option::Some(format!(#fmt, #args)),
format_ident!("_{}", index) }
} } else {
}; quote! {
quote! { message: std::option::Option::None,
message: std::option::Option::Some(#m.as_ref()), }
} };
}
})
.unwrap_or_else(|| {
quote! {
message: std::option::Option::None,
}
});
// Source field // Source field
let src_ident = match &snippet.source { let src_ident = match &snippet.source {
syn::Member::Named(id) => id.clone(), syn::Member::Named(id) => id.clone(),
@ -263,15 +339,24 @@ impl Snippets {
// Highlights // Highlights
let highlights = snippet.highlights.iter().map(|highlight| { let highlights = snippet.highlights.iter().map(|highlight| {
let Highlight { highlight } = highlight; let Highlight { highlight, label } = highlight;
let m = match highlight { let m = match highlight {
syn::Member::Named(id) => id.clone(), syn::Member::Named(id) => id.clone(),
syn::Member::Unnamed(syn::Index { index, .. }) => { syn::Member::Unnamed(syn::Index { index, .. }) => {
format_ident!("_{}", index) format_ident!("_{}", index)
} }
}; };
quote! { if let Some(Display { fmt, args, ..}) = label {
#m.clone().into() quote! {
(
std::option::Option::Some(format!(#fmt, #args)),
#m.clone().into()
)
}
} else {
quote! {
(std::option::Option::None, #m.clone().into())
}
} }
}); });
let highlights = quote! { let highlights = quote! {

View File

@ -159,11 +159,11 @@ impl GraphicalReportPrinter {
// gutter size. // gutter size.
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new); let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
// sorting is your friend. // sorting is your friend.
highlights.sort_unstable_by_key(|h| h.offset()); highlights.sort_unstable_by_key(|(_, h)| h.offset());
let highlights = highlights let highlights = highlights
.into_iter() .into_iter()
.zip(self.theme.styles.highlights.iter().cloned().cycle()) .zip(self.theme.styles.highlights.iter().cloned().cycle())
.map(|(hl, st)| FancySpan::new(hl, st)) .map(|((label, hl), st)| FancySpan::new(label, hl, st))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// The max number of gutter-lines that will be active at any given // The max number of gutter-lines that will be active at any given
@ -189,7 +189,7 @@ impl GraphicalReportPrinter {
.len(); .len();
// Header // Header
if let Some(source_name) = snippet.context.label() { if let Some(source_name) = snippet.source.name() {
let source_name = source_name.style(self.theme.styles.filename); let source_name = source_name.style(self.theme.styles.filename);
write!( write!(
f, f,
@ -558,13 +558,14 @@ impl Line {
} }
struct FancySpan { struct FancySpan {
label: Option<String>,
span: SourceSpan, span: SourceSpan,
style: Style, style: Style,
} }
impl FancySpan { impl FancySpan {
fn new(span: SourceSpan, style: Style) -> Self { fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self {
FancySpan { span, style } FancySpan { label, span, style }
} }
fn style(&self) -> Style { fn style(&self) -> Style {
@ -572,7 +573,9 @@ impl FancySpan {
} }
fn label(&self) -> Option<String> { fn label(&self) -> Option<String> {
self.span.label().map(|l| l.style(self.style()).to_string()) self.label
.as_ref()
.map(|l| l.style(self.style()).to_string())
} }
fn offset(&self) -> usize { fn offset(&self) -> usize {

View File

@ -1,12 +1,10 @@
/*! /*!
Reporters included with `miette`. Reporters included with `miette`.
*/ */
use std::fmt;
use atty::Stream; use atty::Stream;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use crate::protocol::{Diagnostic, DiagnosticReportPrinter, Severity}; use crate::protocol::DiagnosticReportPrinter;
use crate::MietteError; use crate::MietteError;
// NOTE(zkat): I don't understand why these three are "unreachable" when // NOTE(zkat): I don't understand why these three are "unreachable" when
@ -55,41 +53,3 @@ fn get_default_printer() -> Box<dyn DiagnosticReportPrinter + Send + Sync + 'sta
Box::new(NarratableReportPrinter) Box::new(NarratableReportPrinter)
} }
} }
/// Literally what it says on the tin.
#[derive(Debug, Clone)]
pub struct JokePrinter;
impl DiagnosticReportPrinter for JokePrinter {
fn debug(&self, diagnostic: &(dyn Diagnostic), f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
return fmt::Debug::fmt(diagnostic, f);
}
let sev = match diagnostic.severity() {
Some(Severity::Error) | None => "error",
Some(Severity::Warning) => "warning",
Some(Severity::Advice) => "advice",
};
writeln!(
f,
"me, with {} {}: {}",
sev,
diagnostic,
diagnostic
.help()
.unwrap_or_else(|| Box::new(&"have you tried not failing?"))
)?;
writeln!(
f,
"miette, her eyes enormous: you {} miette? you {}? oh! oh! jail for mother! jail for mother for One Thousand Years!!!!",
diagnostic.code(),
diagnostic.snippets().map(|snippets| {
snippets.map(|snippet| snippet.message.map(|x| x.to_owned()))
.collect::<Option<Vec<String>>>()
}).flatten().map(|x| x.join(", ")).unwrap_or_else(||"try and cause miette to panic".into())
)?;
Ok(())
}
}

View File

@ -90,7 +90,7 @@ impl NarratableReportPrinter {
let (contents, lines) = self.get_lines(snippet)?; let (contents, lines) = self.get_lines(snippet)?;
write!(f, "Begin snippet")?; write!(f, "Begin snippet")?;
if let Some(filename) = snippet.context.label() { if let Some(filename) = snippet.source.name() {
write!(f, " for {}", filename,)?; write!(f, " for {}", filename,)?;
} }
writeln!( writeln!(
@ -106,13 +106,13 @@ impl NarratableReportPrinter {
// gutter size. // gutter size.
let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new); let mut highlights = snippet.highlights.clone().unwrap_or_else(Vec::new);
// sorting is your friend. // sorting is your friend.
highlights.sort_unstable_by_key(|h| h.offset()); highlights.sort_unstable_by_key(|(_, h)| h.offset());
// Now it's time for the fun part--actually rendering everything! // Now it's time for the fun part--actually rendering everything!
for line in &lines { for line in &lines {
writeln!(f, "snippet line {}: {}", line.line_number, line.text)?; writeln!(f, "snippet line {}: {}", line.line_number, line.text)?;
let relevant = highlights.iter().filter(|hl| line.span_starts(hl)); let relevant = highlights.iter().filter(|(_, hl)| line.span_starts(hl));
for hl in relevant { for (label, hl) in relevant {
let contents = snippet.source.read_span(hl).map_err(|_| fmt::Error)?; let contents = snippet.source.read_span(hl).map_err(|_| fmt::Error)?;
if contents.line() + 1 == line.line_number { if contents.line() + 1 == line.line_number {
write!( write!(
@ -121,7 +121,7 @@ impl NarratableReportPrinter {
contents.line() + 1, contents.line() + 1,
contents.column() + 1 contents.column() + 1
)?; )?;
if let Some(label) = hl.label() { if let Some(label) = label {
write!(f, ": {}", label)?; write!(f, ": {}", label)?;
} }
writeln!(f)?; writeln!(f)?;

View File

@ -45,7 +45,7 @@ pub trait Diagnostic: std::error::Error {
/// Additional contextual snippets. This is typically used for adding /// Additional contextual snippets. This is typically used for adding
/// marked-up source file output the way compilers often do. /// marked-up source file output the way compilers often do.
fn snippets(&self) -> Option<Box<dyn Iterator<Item = DiagnosticSnippet<'_>> + '_>> { fn snippets<'a>(&'a self) -> Option<Box<dyn Iterator<Item = DiagnosticSnippet<'a>> + 'a>> {
None None
} }
} }
@ -157,6 +157,11 @@ pub trait Source: std::fmt::Debug + Send + Sync {
&'a self, &'a self,
span: &SourceSpan, span: &SourceSpan,
) -> Result<Box<dyn SpanContents + 'a>, MietteError>; ) -> Result<Box<dyn SpanContents + 'a>, MietteError>;
/// Optional name, usually a filename, for this source.
fn name(&self) -> Option<String> {
None
}
} }
/** /**
@ -212,7 +217,7 @@ A snippet from a [Source] to be displayed with a message and possibly some highl
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DiagnosticSnippet<'a> { pub struct DiagnosticSnippet<'a> {
/// Explanation of this specific diagnostic snippet. /// Explanation of this specific diagnostic snippet.
pub message: Option<&'a str>, pub message: Option<String>,
/// A [Source] that can be used to read the actual text of a source. /// A [Source] that can be used to read the actual text of a source.
pub source: &'a (dyn Source), pub source: &'a (dyn Source),
/// The primary [SourceSpan] where this diagnostic is located. /// The primary [SourceSpan] where this diagnostic is located.
@ -220,7 +225,7 @@ pub struct DiagnosticSnippet<'a> {
/// Additional [SourceSpan]s that mark specific sections of the span, for /// Additional [SourceSpan]s that mark specific sections of the span, for
/// example, to underline specific text within the larger span. They're /// example, to underline specific text within the larger span. They're
/// paired with labels that should be applied to those sections. /// paired with labels that should be applied to those sections.
pub highlights: Option<Vec<SourceSpan>>, pub highlights: Option<Vec<(Option<String>, SourceSpan)>>,
} }
/** /**
@ -228,9 +233,6 @@ Span within a [Source] with an associated message.
*/ */
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SourceSpan { pub struct SourceSpan {
/// An optional label for this span. Rendered differently depending on
/// context.
label: Option<String>,
/// The start of the span. /// The start of the span.
offset: SourceOffset, offset: SourceOffset,
/// The total length of the span. Think of this as an offset from `start`. /// The total length of the span. Think of this as an offset from `start`.
@ -241,16 +243,6 @@ impl SourceSpan {
/// Create a new [SourceSpan]. /// Create a new [SourceSpan].
pub fn new(start: SourceOffset, length: SourceOffset) -> Self { pub fn new(start: SourceOffset, length: SourceOffset) -> Self {
Self { Self {
label: None,
offset: start,
length,
}
}
/// Create a new [SourceSpan] with a label.
pub fn new_labeled(label: impl AsRef<str>, start: SourceOffset, length: SourceOffset) -> Self {
Self {
label: Some(label.as_ref().into()),
offset: start, offset: start,
length, length,
} }
@ -261,15 +253,6 @@ impl SourceSpan {
self.offset.offset() 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. /// Total length of the [SourceSpan], in bytes.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.length.offset() self.length.offset()
@ -285,17 +268,6 @@ impl SourceSpan {
impl From<(ByteOffset, ByteOffset)> for SourceSpan { impl From<(ByteOffset, ByteOffset)> for SourceSpan {
fn from((start, len): (ByteOffset, ByteOffset)) -> Self { fn from((start, len): (ByteOffset, ByteOffset)) -> Self {
Self { Self {
label: None,
offset: start.into(),
length: len.into(),
}
}
}
impl<T: AsRef<str>> From<(T, ByteOffset, ByteOffset)> for SourceSpan {
fn from((label, start, len): (T, ByteOffset, ByteOffset)) -> Self {
Self {
label: Some(label.as_ref().into()),
offset: start.into(), offset: start.into(),
length: len.into(), length: len.into(),
} }
@ -305,17 +277,6 @@ impl<T: AsRef<str>> From<(T, ByteOffset, ByteOffset)> for SourceSpan {
impl From<(SourceOffset, SourceOffset)> for SourceSpan { impl From<(SourceOffset, SourceOffset)> for SourceSpan {
fn from((start, len): (SourceOffset, SourceOffset)) -> Self { fn from((start, len): (SourceOffset, SourceOffset)) -> Self {
Self { Self {
label: None,
offset: start,
length: len,
}
}
}
impl<T: AsRef<str>> From<(T, SourceOffset, SourceOffset)> for SourceSpan {
fn from((label, start, len): (T, SourceOffset, SourceOffset)) -> Self {
Self {
label: Some(label.as_ref().into()),
offset: start, offset: start,
length: len, length: len,
} }

View File

@ -2,7 +2,7 @@ use std::fmt;
use thiserror::Error; use thiserror::Error;
use crate::{Diagnostic, DiagnosticReport}; use crate::{Diagnostic, DiagnosticReport, Source};
/// Convenience alias. This is intended to be used as the return type for `main()` /// Convenience alias. This is intended to be used as the return type for `main()`
pub type DiagnosticResult<T> = Result<T, DiagnosticReport>; pub type DiagnosticResult<T> = Result<T, DiagnosticReport>;
@ -47,3 +47,40 @@ impl<T, E: std::error::Error + Send + Sync + 'static> IntoDiagnostic<T, E> for R
}) })
} }
} }
/// Utility struct for when you have a regular [Source] type, such as a String,
/// that doesn't implement `name`, or if you want to override the `.name()`
/// returned by the `Source`.
#[derive(Debug)]
pub struct NamedSource {
source: Box<dyn Source + Send + Sync + 'static>,
name: String,
}
impl NamedSource {
/// Create a new [NamedSource] using a regular [Source] and giving it a [Source::name].
pub fn new(name: impl AsRef<str>, source: impl Source + Send + Sync + 'static) -> Self {
Self {
source: Box::new(source),
name: name.as_ref().to_string(),
}
}
/// Returns a reference the inner [Source] type for this [NamedSource].
pub fn inner(&self) -> &(dyn Source + Send + Sync + 'static) {
&*self.source
}
}
impl Source for NamedSource {
fn read_span<'a>(
&'a self,
span: &crate::SourceSpan,
) -> Result<Box<dyn crate::SpanContents + 'a>, crate::MietteError> {
self.source.read_span(span)
}
fn name(&self) -> Option<String> {
Some(self.name.clone())
}
}

View File

@ -212,7 +212,7 @@ fn test_snippet_named_struct() {
// / [my_snippet]: hi this is where the thing went wrong. // / [my_snippet]: hi this is where the thing went wrong.
// 1 | hello // 1 | hello
// 2 | world // 2 | world
#[snippet(src, "hi this is where the thing went wrong")] #[snippet(src, message("hi this is where the thing went wrong"))]
snip: SourceSpan, // Defines filename using `label` snip: SourceSpan, // Defines filename using `label`
// "Highlights" are the specific highlights _inside_ the snippet. // "Highlights" are the specific highlights _inside_ the snippet.
@ -230,13 +230,7 @@ fn test_snippet_named_struct() {
var1: SourceSpan, var1: SourceSpan,
#[highlight(snip)] #[highlight(snip)]
// Anything that's Clone + Into<SourceSpan> can be used here. // Anything that's Clone + Into<SourceSpan> can be used here.
var2: (String, usize, usize), var2: (usize, usize),
// Now with member source names
filename: String,
second_message: String,
#[snippet(src, filename, second_message)]
snip2: SourceSpan,
} }
} }
@ -247,12 +241,12 @@ fn test_snippet_unnamed_struct() {
#[diagnostic(code(foo::bar::baz))] #[diagnostic(code(foo::bar::baz))]
struct Foo( struct Foo(
String, String,
#[snippet(0, "hi")] SourceSpan, #[snippet(0, message("hi"))] SourceSpan,
#[highlight(1)] SourceSpan, #[highlight(1)] SourceSpan,
#[highlight(1)] SourceSpan, #[highlight(1)] SourceSpan,
// referenced source name // referenced source name
String, String,
#[snippet(0, 4)] SourceSpan, #[snippet(0, message("{}", self.4))] SourceSpan,
#[highlight(5)] SourceSpan, #[highlight(5)] SourceSpan,
#[highlight(5)] SourceSpan, #[highlight(5)] SourceSpan,
); );
@ -267,29 +261,23 @@ fn test_snippet_enum() {
#[diagnostic(code(foo::a))] #[diagnostic(code(foo::a))]
A { A {
src: String, src: String,
#[snippet(src, "hi this is where the thing went wrong")] #[snippet(src, message("hi this is where the thing went wrong"))]
snip: SourceSpan, snip: SourceSpan,
#[highlight(snip)] #[highlight(snip)]
var1: SourceSpan, var1: SourceSpan,
#[highlight(snip)] #[highlight(snip)]
var2: SourceSpan, var2: SourceSpan,
filename: String,
second_message: String,
#[snippet(src, filename, second_message)]
snip2: SourceSpan,
}, },
#[diagnostic(code(foo::b))] #[diagnostic(code(foo::b))]
B( B(
String, String,
#[snippet(0, "hi")] SourceSpan, #[snippet(0, message("hi"))] SourceSpan,
#[highlight(1)] SourceSpan, #[highlight(1)] SourceSpan,
#[highlight(1, "var 2")] SourceSpan, #[highlight(1, label("var 2"))] SourceSpan,
// referenced source name // referenced source name
String, #[snippet(0)] SourceSpan,
String, #[highlight(4)] SourceSpan,
#[snippet(0, 4, 5)] SourceSpan, #[highlight(4)] SourceSpan,
#[highlight(6)] SourceSpan,
#[highlight(6)] SourceSpan,
), ),
} }
} }

View File

@ -1,5 +1,5 @@
use miette::{ use miette::{
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource,
NarratableReportPrinter, SourceSpan, NarratableReportPrinter, SourceSpan,
}; };
use thiserror::Error; use thiserror::Error;
@ -25,19 +25,19 @@ fn single_line_highlight() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight: SourceSpan, highlight: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: ("this bit here", 9, 4).into(), highlight: (9, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -67,8 +67,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx)]
highlight: SourceSpan, highlight: SourceSpan,
@ -77,8 +77,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: (9, 4).into(), highlight: (9, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
@ -109,22 +109,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text text text text text\n here".to_string(); let src = "source\n text text text text text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 9, 4).into(), highlight1: (9, 4).into(),
highlight2: ("also this bit", 14, 4).into(), highlight2: (14, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -155,19 +155,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "these two lines")]
highlight: SourceSpan, highlight: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: ("these two lines", 9, 11).into(), highlight: (9, 11).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -197,12 +197,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 1")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 2")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
@ -215,10 +215,10 @@ line5
.to_string(); .to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("block 1", 0, len).into(), highlight1: (0, len).into(),
highlight2: ("block 2", 10, 9).into(), highlight2: (10, 9).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -251,10 +251,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 1")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx)]
highlight2: SourceSpan, highlight2: SourceSpan,
@ -269,9 +269,9 @@ line5
.to_string(); .to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("block 1", 0, len).into(), highlight1: (0, len).into(),
highlight2: (10, 9).into(), highlight2: (10, 9).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
@ -305,22 +305,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here\nmore here".to_string(); let src = "source\n text\n here\nmore here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 10).into(), highlight1: (0, 10).into(),
highlight2: ("also this bit", 20, 6).into(), highlight2: (20, 6).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError>
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 8).into(), highlight1: (0, 8).into(),
highlight2: ("also this bit", 9, 10).into(), highlight2: (9, 10).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -402,22 +402,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 8).into(), highlight1: (0, 8).into(),
highlight2: ("also this bit", 10, 10).into(), highlight2: (10, 10).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);

View File

@ -1,5 +1,5 @@
use miette::{ use miette::{
Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, Diagnostic, DiagnosticReport, GraphicalReportPrinter, GraphicalTheme, MietteError, NamedSource,
NarratableReportPrinter, SourceSpan, NarratableReportPrinter, SourceSpan,
}; };
use thiserror::Error; use thiserror::Error;
@ -29,19 +29,19 @@ fn single_line_highlight() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight: SourceSpan, highlight: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: ("this bit here", 9, 4).into(), highlight: (9, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -71,8 +71,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx)]
highlight: SourceSpan, highlight: SourceSpan,
@ -81,8 +81,8 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: (9, 4).into(), highlight: (9, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
@ -112,22 +112,22 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text text text text text\n here".to_string(); let src = "source\n text text text text text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 9, 4).into(), highlight1: (9, 4).into(),
highlight2: ("also this bit", 14, 4).into(), highlight2: (14, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -158,19 +158,19 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "these two lines")]
highlight: SourceSpan, highlight: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight: ("these two lines", 9, 11).into(), highlight: (9, 11).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -199,12 +199,12 @@ fn multiline_highlight_flyby() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 1")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 2")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
@ -217,10 +217,10 @@ line5
.to_string(); .to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("block 1", 0, len).into(), highlight1: (0, len).into(),
highlight2: ("block 2", 10, 9).into(), highlight2: (10, 9).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -252,10 +252,10 @@ fn multiline_highlight_no_label() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "block 1")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx)]
highlight2: SourceSpan, highlight2: SourceSpan,
@ -270,9 +270,9 @@ line5
.to_string(); .to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("block 1", 0, len).into(), highlight1: (0, len).into(),
highlight2: (10, 9).into(), highlight2: (10, 9).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
@ -304,22 +304,22 @@ fn multiple_multiline_highlights_adjacent() -> Result<(), MietteError> {
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here\nmore here".to_string(); let src = "source\n text\n here\nmore here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 10).into(), highlight1: (0, 10).into(),
highlight2: ("also this bit", 20, 6).into(), highlight2: (20, 6).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -354,22 +354,22 @@ fn multiple_multiline_highlights_overlapping_lines() -> Result<(), MietteError>
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 8).into(), highlight1: (0, 8).into(),
highlight2: ("also this bit", 9, 10).into(), highlight2: (9, 10).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);
@ -385,22 +385,22 @@ fn multiple_multiline_highlights_overlapping_offsets() -> Result<(), MietteError
#[error("oops!")] #[error("oops!")]
#[diagnostic(code(oops::my::bad), help("try doing it better next time?"))] #[diagnostic(code(oops::my::bad), help("try doing it better next time?"))]
struct MyBad { struct MyBad {
src: String, src: NamedSource,
#[snippet(src, "This is the part that broke")] #[snippet(src, message("This is the part that broke"))]
ctx: SourceSpan, ctx: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "this bit here")]
highlight1: SourceSpan, highlight1: SourceSpan,
#[highlight(ctx)] #[highlight(ctx, label = "also this bit")]
highlight2: SourceSpan, highlight2: SourceSpan,
} }
let src = "source\n text\n here".to_string(); let src = "source\n text\n here".to_string();
let len = src.len(); let len = src.len();
let err = MyBad { let err = MyBad {
src, src: NamedSource::new("bad_file.rs", src),
ctx: ("bad_file.rs", 0, len).into(), ctx: (0, len).into(),
highlight1: ("this bit here", 0, 8).into(), highlight1: (0, 8).into(),
highlight2: ("also this bit", 10, 10).into(), highlight2: (10, 10).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("{}", out); println!("{}", out);