From 09d7d15098ea3fbf258795b6912f0bb37a18f3ac Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 5 May 2023 21:37:30 +0800 Subject: [PATCH 01/30] Remove dot --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b38d0292..87215d69 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,7 +23,7 @@ ## 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. ๐Ÿ’š From e6d2eede6171e1ebd4dd27ce1197ccb922964bbf Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 5 May 2023 22:46:50 +0800 Subject: [PATCH 02/30] Add skeleton --- src/dynamic_diagnostic.rs | 72 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 ++- 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/dynamic_diagnostic.rs diff --git a/src/dynamic_diagnostic.rs b/src/dynamic_diagnostic.rs new file mode 100644 index 00000000..87055b1f --- /dev/null +++ b/src/dynamic_diagnostic.rs @@ -0,0 +1,72 @@ +use std::{ + error::Error, + fmt::{Debug, Display}, +}; + +use crate::Diagnostic; + +/// Diagnostic that can be created at runtime. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DynamicDiagnostic { + /// Displayed diagnostic description + pub description: 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, +} + +impl Display for DynamicDiagnostic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.description) + } +} + +impl Error for DynamicDiagnostic {} + +impl Diagnostic for DynamicDiagnostic { + fn code<'a>(&'a self) -> Option> { + self.code + .as_ref() + .map(Box::new) + .map(|c| c as Box) + } +} + +impl DynamicDiagnostic { + /// Create a new dynamic diagnostic with the given description. + /// + /// # Examples + /// ``` + /// use miette::{Diagnostic, DynamicDiagnostic}; + /// + /// let diag = DynamicDiagnostic::new("Oops, something went wrong!"); + /// assert_eq!(diag.to_string(), "Oops, something went wrong!"); + /// assert_eq!(diag.description, "Oops, something went wrong!"); + /// ``` + pub fn new(description: impl Into) -> Self { + Self { + description: description.into(), + code: None, + } + } + + /// Return new diagnostic with the given code. + /// + /// # Examples + /// ``` + /// use miette::{Diagnostic, DynamicDiagnostic}; + /// + /// let diag = DynamicDiagnostic::new("Oops, something went wrong!").with_code("foo::bar::baz"); + /// assert_eq!(diag.description, "Oops, something went wrong!"); + /// assert_eq!(diag.code, Some("foo::bar::baz".to_string())); + /// ``` + pub fn with_code(self, code: impl Into) -> Self { + Self { + code: Some(code.into()), + ..self + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b18239a5..c06f25d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -249,7 +249,7 @@ //! To construct your own simple adhoc error use the [miette!] macro: //! ```rust //! // my_app/lib/my_internal_file.rs -//! use miette::{IntoDiagnostic, Result, WrapErr, miette}; +//! use miette::{miette, IntoDiagnostic, Result, WrapErr}; //! use semver::Version; //! //! pub fn some_tool() -> Result { @@ -619,6 +619,7 @@ //! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed. pub use miette_derive::*; +pub use dynamic_diagnostic::*; pub use error::*; pub use eyreish::*; #[cfg(feature = "fancy-no-backtrace")] @@ -631,6 +632,7 @@ pub use protocol::*; mod chain; mod diagnostic_chain; +mod dynamic_diagnostic; mod error; mod eyreish; #[cfg(feature = "fancy-no-backtrace")] From ab20069e8513d62d24836b372b2c5103f0692461 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 09:20:46 +0800 Subject: [PATCH 03/30] DynamicDiagnostic -> MietteDiagnostic --- src/lib.rs | 4 ++-- ...amic_diagnostic.rs => miette_diagnostic.rs} | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) rename src/{dynamic_diagnostic.rs => miette_diagnostic.rs} (79%) diff --git a/src/lib.rs b/src/lib.rs index c06f25d8..576df49b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -619,12 +619,12 @@ //! [`ariadne`](https://github.com/zesterer/ariadne), which is MIT licensed. pub use miette_derive::*; -pub use dynamic_diagnostic::*; pub use error::*; pub use eyreish::*; #[cfg(feature = "fancy-no-backtrace")] pub use handler::*; pub use handlers::*; +pub use miette_diagnostic::*; pub use named_source::*; #[cfg(feature = "fancy")] pub use panic::*; @@ -632,7 +632,6 @@ pub use protocol::*; mod chain; mod diagnostic_chain; -mod dynamic_diagnostic; mod error; mod eyreish; #[cfg(feature = "fancy-no-backtrace")] @@ -640,6 +639,7 @@ mod handler; mod handlers; #[doc(hidden)] pub mod macro_helpers; +mod miette_diagnostic; mod named_source; #[cfg(feature = "fancy")] mod panic; diff --git a/src/dynamic_diagnostic.rs b/src/miette_diagnostic.rs similarity index 79% rename from src/dynamic_diagnostic.rs rename to src/miette_diagnostic.rs index 87055b1f..486ad6e3 100644 --- a/src/dynamic_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -7,7 +7,7 @@ use crate::Diagnostic; /// Diagnostic that can be created at runtime. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct DynamicDiagnostic { +pub struct MietteDiagnostic { /// Displayed diagnostic description pub description: String, /// Unique diagnostic code to look up more information @@ -18,15 +18,15 @@ pub struct DynamicDiagnostic { pub code: Option, } -impl Display for DynamicDiagnostic { +impl Display for MietteDiagnostic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", &self.description) } } -impl Error for DynamicDiagnostic {} +impl Error for MietteDiagnostic {} -impl Diagnostic for DynamicDiagnostic { +impl Diagnostic for MietteDiagnostic { fn code<'a>(&'a self) -> Option> { self.code .as_ref() @@ -35,14 +35,14 @@ impl Diagnostic for DynamicDiagnostic { } } -impl DynamicDiagnostic { +impl MietteDiagnostic { /// Create a new dynamic diagnostic with the given description. /// /// # Examples /// ``` - /// use miette::{Diagnostic, DynamicDiagnostic}; + /// use miette::{Diagnostic, MietteDiagnostic}; /// - /// let diag = DynamicDiagnostic::new("Oops, something went wrong!"); + /// let diag = MietteDiagnostic::new("Oops, something went wrong!"); /// assert_eq!(diag.to_string(), "Oops, something went wrong!"); /// assert_eq!(diag.description, "Oops, something went wrong!"); /// ``` @@ -57,9 +57,9 @@ impl DynamicDiagnostic { /// /// # Examples /// ``` - /// use miette::{Diagnostic, DynamicDiagnostic}; + /// use miette::{Diagnostic, MietteDiagnostic}; /// - /// let diag = DynamicDiagnostic::new("Oops, something went wrong!").with_code("foo::bar::baz"); + /// let diag = MietteDiagnostic::new("Oops, something went wrong!").with_code("foo::bar::baz"); /// assert_eq!(diag.description, "Oops, something went wrong!"); /// assert_eq!(diag.code, Some("foo::bar::baz".to_string())); /// ``` From 01b34a4f75d0b9e4cdfbac7fbf3af4836e52d013 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 09:30:24 +0800 Subject: [PATCH 04/30] Make `Severity::Error` to be default severity --- src/protocol.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/protocol.rs b/src/protocol.rs index 53312497..d8f9195d 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -160,7 +160,7 @@ impl From> for Box Self { + Severity::Error + } +} + /** Represents readable source code of some sort. From 61d43f8b17d8de3ce97a4712be5accdd0caacf29 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 09:34:45 +0800 Subject: [PATCH 05/30] Add severity field --- src/miette_diagnostic.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 486ad6e3..6addfb02 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -3,7 +3,7 @@ use std::{ fmt::{Debug, Display}, }; -use crate::Diagnostic; +use crate::{Diagnostic, Severity}; /// Diagnostic that can be created at runtime. #[derive(Debug, Clone, PartialEq, Eq)] @@ -16,6 +16,10 @@ pub struct MietteDiagnostic { /// Rust path format (`foo::bar::baz`) is recommended, but more classic /// codes like `E0123` will work just fine. pub code: Option, + /// [`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: Severity, } impl Display for MietteDiagnostic { @@ -33,6 +37,10 @@ impl Diagnostic for MietteDiagnostic { .map(Box::new) .map(|c| c as Box) } + + fn severity(&self) -> Option { + Some(self.severity) + } } impl MietteDiagnostic { @@ -40,16 +48,18 @@ impl MietteDiagnostic { /// /// # Examples /// ``` - /// use miette::{Diagnostic, MietteDiagnostic}; + /// 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.description, "Oops, something went wrong!"); + /// assert_eq!(diag.severity, Severity::Error); /// ``` pub fn new(description: impl Into) -> Self { Self { description: description.into(), code: None, + severity: Severity::Error, } } @@ -69,4 +79,18 @@ impl MietteDiagnostic { ..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.description, "I warn you to stop!"); + /// assert_eq!(diag.severity, Severity::Warning); + /// ``` + pub fn with_severity(self, severity: Severity) -> Self { + Self { severity, ..self } + } } From 53b21ead3976704d5138f9f8a3adc553204b9b90 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 09:43:46 +0800 Subject: [PATCH 06/30] Add help field --- src/miette_diagnostic.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 6addfb02..bd8072d3 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -14,12 +14,14 @@ pub struct MietteDiagnostic { /// 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. + /// codes like `E0123` will work just fine pub code: Option, /// [`Diagnostic`] severity. Intended to be used by /// [`ReportHandler`](crate::ReportHandler)s to change the way different - /// [`Diagnostic`]s are displayed. Defaults to [`Severity::Error`]. + /// [`Diagnostic`]s are displayed. Defaults to [`Severity::Error`] pub severity: Severity, + /// Additional help text related to this Diagnostic + pub help: Option, } impl Display for MietteDiagnostic { @@ -41,6 +43,13 @@ impl Diagnostic for MietteDiagnostic { fn severity(&self) -> Option { Some(self.severity) } + + fn help<'a>(&'a self) -> Option> { + self.help + .as_ref() + .map(Box::new) + .map(|c| c as Box) + } } impl MietteDiagnostic { @@ -58,8 +67,9 @@ impl MietteDiagnostic { pub fn new(description: impl Into) -> Self { Self { description: description.into(), - code: None, severity: Severity::Error, + code: None, + help: None, } } @@ -93,4 +103,21 @@ impl MietteDiagnostic { pub fn with_severity(self, severity: Severity) -> Self { Self { 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.description, "PC is not working"); + /// assert_eq!(diag.help, Some("Try to reboot it again".to_string())); + /// ``` + pub fn with_help(self, help: impl Into) -> Self { + Self { + help: Some(help.into()), + ..self + } + } } From a1f602c0fec58865492c8d87783e5cc301d5e5df Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 09:49:36 +0800 Subject: [PATCH 07/30] Add url field --- src/miette_diagnostic.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index bd8072d3..a21f1ced 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -22,6 +22,9 @@ pub struct MietteDiagnostic { pub severity: Severity, /// Additional help text related to this Diagnostic pub help: Option, + /// URL to visit for a more detailed explanation/help about this + /// [`Diagnostic`]. + pub url: Option, } impl Display for MietteDiagnostic { @@ -50,6 +53,13 @@ impl Diagnostic for MietteDiagnostic { .map(Box::new) .map(|c| c as Box) } + + fn url<'a>(&'a self) -> Option> { + self.url + .as_ref() + .map(Box::new) + .map(|c| c as Box) + } } impl MietteDiagnostic { @@ -70,6 +80,7 @@ impl MietteDiagnostic { severity: Severity::Error, code: None, help: None, + url: None, } } @@ -120,4 +131,25 @@ impl MietteDiagnostic { ..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.description, "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(self, url: impl Into) -> Self { + Self { + url: Some(url.into()), + ..self + } + } } From c6e977bbb82eec74ff3af44659855e44aac91b8e Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 10:11:40 +0800 Subject: [PATCH 08/30] Add labels field --- src/miette_diagnostic.rs | 49 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index a21f1ced..f98616ee 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -3,7 +3,7 @@ use std::{ fmt::{Debug, Display}, }; -use crate::{Diagnostic, Severity}; +use crate::{Diagnostic, LabeledSpan, Severity}; /// Diagnostic that can be created at runtime. #[derive(Debug, Clone, PartialEq, Eq)] @@ -25,6 +25,8 @@ pub struct MietteDiagnostic { /// URL to visit for a more detailed explanation/help about this /// [`Diagnostic`]. pub url: Option, + /// Labels to apply to this `Diagnostic`'s [`Diagnostic::source_code`] + pub labels: Vec, } impl Display for MietteDiagnostic { @@ -60,6 +62,10 @@ impl Diagnostic for MietteDiagnostic { .map(Box::new) .map(|c| c as Box) } + + fn labels(&self) -> Option + '_>> { + Some(Box::new(self.labels.iter().cloned())) + } } impl MietteDiagnostic { @@ -78,6 +84,7 @@ impl MietteDiagnostic { Self { description: description.into(), severity: Severity::Error, + labels: Vec::new(), code: None, help: None, url: None, @@ -152,4 +159,44 @@ impl MietteDiagnostic { ..self } } + + /// Return new diagnostic with the given label + /// + /// # Examples + /// ``` + /// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic}; + /// + /// let source = "cpp is the best language"; + /// + /// let label = LabeledSpan::new(Some("This should be Rust".to_string()), 0, 3); + /// let diag = MietteDiagnostic::new("Wrong best language").with_label(label.clone()); + /// assert_eq!(diag.description, "Wrong best language"); + /// assert_eq!(diag.labels, vec![label]); + /// ``` + pub fn with_label(self, label: impl Into) -> Self { + Self { + labels: vec![label.into()], + ..self + } + } + + /// Return new diagnostic with the given labels + /// + /// # Examples + /// ``` + /// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic}; + /// + /// let source = "helo word"; + /// + /// let labels = vec![ + /// LabeledSpan::new(Some("add 'l'".to_string()), 3, 0), + /// LabeledSpan::new(Some("add 'l'".to_string()), 8, 0), + /// ]; + /// let diag = MietteDiagnostic::new("Typos in 'hello world'").with_labels(labels.clone()); + /// assert_eq!(diag.description, "Typos in 'hello world'"); + /// assert_eq!(diag.labels, labels); + /// ``` + pub fn with_labels(self, labels: Vec) -> Self { + Self { labels, ..self } + } } From a3ee52a3be957705ca7e489253f2c16c7a7a0eb0 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 10:29:44 +0800 Subject: [PATCH 09/30] Add convenience function to `LabeledSpan` --- src/protocol.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/protocol.rs b/src/protocol.rs index d8f9195d..c471b74d 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -225,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, label: impl Into) -> 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) -> Self { + Self::new(Some(label.into()), offset.into(), 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) -> Self { + Self::new_with_span(None, span) + } + /// Gets the (optional) label string for this `LabeledSpan`. pub fn label(&self) -> Option<&str> { self.label.as_deref() From 8aaba9974e93babedeefa4d100b5caa79604111f Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 10:32:35 +0800 Subject: [PATCH 10/30] Use convenience functions in examples --- src/miette_diagnostic.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index f98616ee..8ec7de6b 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -160,7 +160,9 @@ impl MietteDiagnostic { } } - /// Return new diagnostic with the given label + /// Return new diagnostic with the given label. + /// + /// Discards previous labels /// /// # Examples /// ``` @@ -168,7 +170,7 @@ impl MietteDiagnostic { /// /// let source = "cpp is the best language"; /// - /// let label = LabeledSpan::new(Some("This should be Rust".to_string()), 0, 3); + /// let label = LabeledSpan::at(0..3, "This should be Rust"); /// let diag = MietteDiagnostic::new("Wrong best language").with_label(label.clone()); /// assert_eq!(diag.description, "Wrong best language"); /// assert_eq!(diag.labels, vec![label]); @@ -180,17 +182,19 @@ impl MietteDiagnostic { } } - /// Return new diagnostic with the given labels + /// Return new diagnostic with the given labels. + /// + /// Discards previous labels /// /// # Examples /// ``` /// use miette::{Diagnostic, LabeledSpan, MietteDiagnostic}; /// - /// let source = "helo word"; + /// let source = "helo wrld"; /// /// let labels = vec![ - /// LabeledSpan::new(Some("add 'l'".to_string()), 3, 0), - /// LabeledSpan::new(Some("add 'l'".to_string()), 8, 0), + /// 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.description, "Typos in 'hello world'"); From 021eb01feb321ecc1bee72ed3aff47c52ceb5c3b Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 12:55:44 +0800 Subject: [PATCH 11/30] Adjust `miette!` a little bit --- src/eyreish/macros.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 0bc5520f..0f3e3dfa 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -130,8 +130,7 @@ macro_rules! ensure { /// string with arguments. It also can take any custom type which implements /// `Debug` and `Display`. /// -/// # Example -/// +/// # Examples /// ``` /// # type V = (); /// # @@ -146,6 +145,11 @@ macro_rules! ensure { /// # Ok(()) /// } /// ``` +/// ``` +/// use miette::miette; +/// +/// let err = miette!("expected '('", code = "expected::lparen"); +/// ``` /// /// ## `anyhow`/`eyre` Users /// @@ -162,6 +166,12 @@ macro_rules! miette { let error = $err; (&error).miette_kind().new(error) }); + ($msg:literal, $(code = $code:literal)? ) => { + $crate::Report::from( + $crate::MietteDiagnostic::new($msg) + $(.with_code($code))? + ) + }; ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc(format!($fmt, $($arg)*)) }; From b9a892f39b22e0e1723e4cf36a735d598fab3cdb Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 12:59:10 +0800 Subject: [PATCH 12/30] Use `Option` --- src/miette_diagnostic.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 8ec7de6b..13048cfb 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -19,7 +19,7 @@ pub struct MietteDiagnostic { /// [`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: Severity, + pub severity: Option, /// Additional help text related to this Diagnostic pub help: Option, /// URL to visit for a more detailed explanation/help about this @@ -46,7 +46,7 @@ impl Diagnostic for MietteDiagnostic { } fn severity(&self) -> Option { - Some(self.severity) + self.severity } fn help<'a>(&'a self) -> Option> { @@ -78,13 +78,12 @@ impl MietteDiagnostic { /// let diag = MietteDiagnostic::new("Oops, something went wrong!"); /// assert_eq!(diag.to_string(), "Oops, something went wrong!"); /// assert_eq!(diag.description, "Oops, something went wrong!"); - /// assert_eq!(diag.severity, Severity::Error); /// ``` pub fn new(description: impl Into) -> Self { Self { description: description.into(), - severity: Severity::Error, labels: Vec::new(), + severity: None, code: None, help: None, url: None, @@ -116,10 +115,13 @@ impl MietteDiagnostic { /// /// let diag = MietteDiagnostic::new("I warn you to stop!").with_severity(Severity::Warning); /// assert_eq!(diag.description, "I warn you to stop!"); - /// assert_eq!(diag.severity, Severity::Warning); + /// assert_eq!(diag.severity, Some(Severity::Warning)); /// ``` pub fn with_severity(self, severity: Severity) -> Self { - Self { severity, ..self } + Self { + severity: Some(severity), + ..self + } } /// Return new diagnostic with the given help message. From c2d793e5787c59e7c9102c65eebdcd285ef04140 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 22:35:17 +0800 Subject: [PATCH 13/30] labels: `Option>` --- src/miette_diagnostic.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 13048cfb..5abf15c7 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -26,7 +26,7 @@ pub struct MietteDiagnostic { /// [`Diagnostic`]. pub url: Option, /// Labels to apply to this `Diagnostic`'s [`Diagnostic::source_code`] - pub labels: Vec, + pub labels: Option>, } impl Display for MietteDiagnostic { @@ -64,7 +64,11 @@ impl Diagnostic for MietteDiagnostic { } fn labels(&self) -> Option + '_>> { - Some(Box::new(self.labels.iter().cloned())) + self.labels + .as_ref() + .map(|ls| ls.iter().cloned()) + .map(Box::new) + .map(|b| b as Box>) } } @@ -82,7 +86,7 @@ impl MietteDiagnostic { pub fn new(description: impl Into) -> Self { Self { description: description.into(), - labels: Vec::new(), + labels: None, severity: None, code: None, help: None, @@ -175,11 +179,11 @@ impl MietteDiagnostic { /// let label = LabeledSpan::at(0..3, "This should be Rust"); /// let diag = MietteDiagnostic::new("Wrong best language").with_label(label.clone()); /// assert_eq!(diag.description, "Wrong best language"); - /// assert_eq!(diag.labels, vec![label]); + /// assert_eq!(diag.labels, Some(vec![label])); /// ``` pub fn with_label(self, label: impl Into) -> Self { Self { - labels: vec![label.into()], + labels: Some(vec![label.into()]), ..self } } @@ -200,9 +204,12 @@ impl MietteDiagnostic { /// ]; /// let diag = MietteDiagnostic::new("Typos in 'hello world'").with_labels(labels.clone()); /// assert_eq!(diag.description, "Typos in 'hello world'"); - /// assert_eq!(diag.labels, labels); + /// assert_eq!(diag.labels, Some(labels)); /// ``` pub fn with_labels(self, labels: Vec) -> Self { - Self { labels, ..self } + Self { + labels: Some(labels), + ..self + } } } From bcf18f81511d3116207cd8ea6413205a29e96edd Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 22:35:53 +0800 Subject: [PATCH 14/30] Fully implement support for `MietteDiagnostic`-like arguments in `miette!` --- src/eyreish/macros.rs | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 0f3e3dfa..212a0bc3 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -124,13 +124,11 @@ macro_rules! ensure { }; } -/// Construct an ad-hoc error from a string. -/// -/// This evaluates to an `Error`. It can take either just a string, or a format -/// string with arguments. It also can take any custom type which implements -/// `Debug` and `Display`. +/// Construct an ad-hoc [`Report`]. /// /// # Examples +/// +/// With string literal and interpolation: /// ``` /// # type V = (); /// # @@ -145,10 +143,22 @@ macro_rules! ensure { /// # Ok(()) /// } /// ``` -/// ``` -/// use miette::miette; /// -/// let err = miette!("expected '('", code = "expected::lparen"); +/// With [`MietteDiagnostic`]-like arguments: +/// ``` +/// use miette::{miette, LabeledSpan, Severity}; +/// +/// let source = "(2 + 2".to_string(); +/// let report = miette!( +/// "expected closing ')'", +/// // 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" +/// ) +/// .with_source_code(source); /// ``` /// /// ## `anyhow`/`eyre` Users @@ -166,12 +176,11 @@ macro_rules! miette { let error = $err; (&error).miette_kind().new(error) }); - ($msg:literal, $(code = $code:literal)? ) => { - $crate::Report::from( - $crate::MietteDiagnostic::new($msg) - $(.with_code($code))? - ) - }; + ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => {{ + let mut diag = $crate::MietteDiagnostic::new(format!("{}", $fmt)); + $(diag.$key = Some($value.into());)* + $crate::Report::from(diag) + }}; ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc(format!($fmt, $($arg)*)) }; From 287ffc54ac7a8e11c9a581c7f5b1948ea42bbb44 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 22:41:33 +0800 Subject: [PATCH 15/30] Add `miette_diagnostic!` --- src/eyreish/macros.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 212a0bc3..90a01612 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -176,12 +176,36 @@ macro_rules! miette { let error = $err; (&error).miette_kind().new(error) }); + ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { + $crate::Report::from($crate::miette_diagnostic!($fmt, $($key = $value,)*)) + }; + ($fmt:expr, $($arg:tt)*) => { + $crate::private::new_adhoc(format!($fmt, $($arg)*)) + }; +} + +/// Construct a [`MietteDiagnostic`] in more user-friendly way. +/// +/// # Examples +/// ``` +/// use miette::{miette_diagnostic, LabeledSpan, Severity}; +/// +/// let source = "(2 + 2".to_string(); +/// let diag = miette_diagnostic!( +/// "expected closing ')'", +/// // 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" +/// ); +/// ``` +#[macro_export] +macro_rules! miette_diagnostic { ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => {{ let mut diag = $crate::MietteDiagnostic::new(format!("{}", $fmt)); $(diag.$key = Some($value.into());)* - $crate::Report::from(diag) + diag }}; - ($fmt:expr, $($arg:tt)*) => { - $crate::private::new_adhoc(format!($fmt, $($arg)*)) - }; } From 4f0bc3e8d3e18009cf2ce7d4b482b182c62ae76d Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 22:58:08 +0800 Subject: [PATCH 16/30] Add `ensure!` support --- src/eyreish/macros.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 90a01612..2d54eccb 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -105,6 +105,19 @@ macro_rules! bail { /// # Ok(()) /// # } /// ``` +/// +/// ``` +/// use miette::{ensure, Result, Severity}; +/// +/// fn divide(x: f64, y: f64) -> Result { +/// ensure!( +/// y.abs() >= 1e-3, +/// "dividing by value close to 0", +/// severity = Severity::Warning +/// ); +/// Ok(x / y) +/// } +/// ``` #[macro_export] macro_rules! ensure { ($cond:expr, $msg:literal $(,)?) => { @@ -117,6 +130,11 @@ macro_rules! ensure { return $crate::private::Err($crate::miette!($err)); } }; + ($cond:expr, $fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { + if !$cond { + return $crate::private::Err($crate::miette!($fmt, $($key = $value),*)); + } + }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { if !$cond { return $crate::private::Err($crate::miette!($fmt, $($arg)*)); From 52e2dcbced882de1f8ea5fa8892ace9a818f7641 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 23:03:31 +0800 Subject: [PATCH 17/30] Add `bail!` support --- src/eyreish/macros.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 2d54eccb..5ad45f2a 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -48,6 +48,17 @@ /// # Ok(()) /// # } /// ``` +/// +/// ``` +/// use miette::{bail, Result, Severity}; +/// +/// fn divide(x: f64, y: f64) -> Result { +/// if y.abs() < 1e-3 { +/// bail!("dividing by value close to 0", severity = Severity::Warning); +/// } +/// Ok(x / y) +/// } +/// ``` #[macro_export] macro_rules! bail { ($msg:literal $(,)?) => { @@ -56,6 +67,9 @@ macro_rules! bail { ($err:expr $(,)?) => { return $crate::private::Err($crate::miette!($err)); }; + ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { + return $crate::private::Err($crate::miette!($fmt, $($key = $value),*)); + }; ($fmt:expr, $($arg:tt)*) => { return $crate::private::Err($crate::miette!($fmt, $($arg)*)); }; From 7c4dd12d8d76b1f16de5620ba26f184553248d0d Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 6 May 2023 23:31:00 +0800 Subject: [PATCH 18/30] Add docs --- src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 576df49b..dca2974c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,7 @@ //! - [... multiple related errors](#-multiple-related-errors) //! - [... delayed source code](#-delayed-source-code) //! - [... handler options](#-handler-options) +//! - [... dynamic diagnostics](#-dynamic-diagnostics) //! - [Acknowledgements](#acknowledgements) //! - [License](#license) //! @@ -590,6 +591,28 @@ //! See the docs for [`MietteHandlerOpts`] for more details on what you can //! 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!`], [`miette_diagnostic!`] macros or +//! [`MietteDiagnostic`] directly to create diagnostic on the fly. +//! +//! ```rs +//! # use miette::{miette, LabeledSpan, Report}; +//! +//! let source = "2 + 2 * 2 = 8".to_string(); +//! let report = miette!( +//! "Wrong answer", +//! labels = vec[ +//! LabeledSpan::at(12..13, "this should be 6"), +//! ], +//! help = "'*' has greater precedence than '+'" +//! ).with_source_code(source); +//! println!("{:?}", report) +//! ``` +//! //! ## Acknowledgements //! //! `miette` was not developed in a void. It owes enormous credit to various From 0e5512ad35ef9b0ef7f6d3b5d5abeaac9acc5613 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 08:59:16 +0800 Subject: [PATCH 19/30] Add dot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kat Marchรกn --- src/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol.rs b/src/protocol.rs index c471b74d..fd5f7b12 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -169,7 +169,7 @@ pub enum Severity { /// Warning. Please take note. Warning, /// Critical failure. The program cannot continue. - /// This is the default severity, if you don't specify another one + /// This is the default severity, if you don't specify another one. Error, } From e35a63a9928b41eb59e914fb8db0c48a1dfccb53 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 09:22:40 +0800 Subject: [PATCH 20/30] description -> message --- src/miette_diagnostic.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 5abf15c7..25c65292 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -8,8 +8,8 @@ use crate::{Diagnostic, LabeledSpan, Severity}; /// Diagnostic that can be created at runtime. #[derive(Debug, Clone, PartialEq, Eq)] pub struct MietteDiagnostic { - /// Displayed diagnostic description - pub description: String, + /// 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. @@ -31,7 +31,7 @@ pub struct MietteDiagnostic { impl Display for MietteDiagnostic { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", &self.description) + write!(f, "{}", &self.message) } } @@ -73,7 +73,7 @@ impl Diagnostic for MietteDiagnostic { } impl MietteDiagnostic { - /// Create a new dynamic diagnostic with the given description. + /// Create a new dynamic diagnostic with the given message. /// /// # Examples /// ``` @@ -81,11 +81,11 @@ impl MietteDiagnostic { /// /// let diag = MietteDiagnostic::new("Oops, something went wrong!"); /// assert_eq!(diag.to_string(), "Oops, something went wrong!"); - /// assert_eq!(diag.description, "Oops, something went wrong!"); + /// assert_eq!(diag.message, "Oops, something went wrong!"); /// ``` - pub fn new(description: impl Into) -> Self { + pub fn new(message: impl Into) -> Self { Self { - description: description.into(), + message: message.into(), labels: None, severity: None, code: None, @@ -101,7 +101,7 @@ impl MietteDiagnostic { /// use miette::{Diagnostic, MietteDiagnostic}; /// /// let diag = MietteDiagnostic::new("Oops, something went wrong!").with_code("foo::bar::baz"); - /// assert_eq!(diag.description, "Oops, something went wrong!"); + /// assert_eq!(diag.message, "Oops, something went wrong!"); /// assert_eq!(diag.code, Some("foo::bar::baz".to_string())); /// ``` pub fn with_code(self, code: impl Into) -> Self { @@ -118,7 +118,7 @@ impl MietteDiagnostic { /// use miette::{Diagnostic, MietteDiagnostic, Severity}; /// /// let diag = MietteDiagnostic::new("I warn you to stop!").with_severity(Severity::Warning); - /// assert_eq!(diag.description, "I warn you to stop!"); + /// assert_eq!(diag.message, "I warn you to stop!"); /// assert_eq!(diag.severity, Some(Severity::Warning)); /// ``` pub fn with_severity(self, severity: Severity) -> Self { @@ -135,7 +135,7 @@ impl MietteDiagnostic { /// use miette::{Diagnostic, MietteDiagnostic}; /// /// let diag = MietteDiagnostic::new("PC is not working").with_help("Try to reboot it again"); - /// assert_eq!(diag.description, "PC is not working"); + /// assert_eq!(diag.message, "PC is not working"); /// assert_eq!(diag.help, Some("Try to reboot it again".to_string())); /// ``` pub fn with_help(self, help: impl Into) -> Self { @@ -153,7 +153,7 @@ impl MietteDiagnostic { /// /// let diag = MietteDiagnostic::new("PC is not working") /// .with_url("https://letmegooglethat.com/?q=Why+my+pc+doesn%27t+work"); - /// assert_eq!(diag.description, "PC is not working"); + /// 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()) @@ -178,7 +178,7 @@ impl MietteDiagnostic { /// /// let label = LabeledSpan::at(0..3, "This should be Rust"); /// let diag = MietteDiagnostic::new("Wrong best language").with_label(label.clone()); - /// assert_eq!(diag.description, "Wrong best language"); + /// assert_eq!(diag.message, "Wrong best language"); /// assert_eq!(diag.labels, Some(vec![label])); /// ``` pub fn with_label(self, label: impl Into) -> Self { @@ -203,7 +203,7 @@ impl MietteDiagnostic { /// LabeledSpan::at_offset(6, "add 'r'"), /// ]; /// let diag = MietteDiagnostic::new("Typos in 'hello world'").with_labels(labels.clone()); - /// assert_eq!(diag.description, "Typos in 'hello world'"); + /// assert_eq!(diag.message, "Typos in 'hello world'"); /// assert_eq!(diag.labels, Some(labels)); /// ``` pub fn with_labels(self, labels: Vec) -> Self { From 4148552cf00ede36fc6a95fb979dff9c289ec20d Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 09:26:02 +0800 Subject: [PATCH 21/30] `miette_diagnostic!` -> `diagnostic!` --- src/eyreish/macros.rs | 8 ++++---- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 5ad45f2a..5a83b097 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -209,7 +209,7 @@ macro_rules! miette { (&error).miette_kind().new(error) }); ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { - $crate::Report::from($crate::miette_diagnostic!($fmt, $($key = $value,)*)) + $crate::Report::from($crate::diagnostic!($fmt, $($key = $value,)*)) }; ($fmt:expr, $($arg:tt)*) => { $crate::private::new_adhoc(format!($fmt, $($arg)*)) @@ -220,10 +220,10 @@ macro_rules! miette { /// /// # Examples /// ``` -/// use miette::{miette_diagnostic, LabeledSpan, Severity}; +/// use miette::{diagnostic, LabeledSpan, Severity}; /// /// let source = "(2 + 2".to_string(); -/// let diag = miette_diagnostic!( +/// let diag = diagnostic!( /// "expected closing ')'", /// // Those fields are optional /// severity = Severity::Error, @@ -234,7 +234,7 @@ macro_rules! miette { /// ); /// ``` #[macro_export] -macro_rules! miette_diagnostic { +macro_rules! diagnostic { ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => {{ let mut diag = $crate::MietteDiagnostic::new(format!("{}", $fmt)); $(diag.$key = Some($value.into());)* diff --git a/src/lib.rs b/src/lib.rs index dca2974c..0c64321e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -596,7 +596,7 @@ //! If you... //! - ...don't know all the possible errors upfront //! - ...need to serialize/deserialize errors -//! then you may want to use [`miette!`], [`miette_diagnostic!`] macros or +//! then you may want to use [`miette!`], [`diagnostic!`] macros or //! [`MietteDiagnostic`] directly to create diagnostic on the fly. //! //! ```rs From b65872011cd3df64a6a0ddfaa2ed05d70d41cf60 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 09:44:23 +0800 Subject: [PATCH 22/30] Add `and_label(s)` --- src/miette_diagnostic.rs | 53 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 25c65292..8d2060e0 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -206,10 +206,61 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Typos in 'hello world'"); /// assert_eq!(diag.labels, Some(labels)); /// ``` - pub fn with_labels(self, labels: Vec) -> Self { + pub fn with_labels(self, labels: impl IntoIterator) -> 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(self, label: impl Into) -> 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(self, labels: impl IntoIterator) -> Self { + let mut all_labels = self.labels.unwrap_or_default(); + all_labels.extend(labels.into_iter()); + Self { + labels: Some(all_labels), + ..self + } + } } From cb5a1d35e0c613d671a751557a9e6a09c2949402 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 10:21:49 +0800 Subject: [PATCH 23/30] Implement interpolation --- src/eyreish/macros.rs | 95 +++++++++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 5a83b097..85da9692 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -16,7 +16,7 @@ /// # let resource = 0; /// # /// if !has_permission(user, resource) { -/// bail!("permission denied for accessing {}", resource); +/// bail!("permission denied for accessing {resource}"); /// } /// # Ok(()) /// # } @@ -54,24 +54,26 @@ /// /// fn divide(x: f64, y: f64) -> Result { /// if y.abs() < 1e-3 { -/// bail!("dividing by value close to 0", severity = Severity::Warning); +/// bail!( +/// severity = Severity::Warning; +/// "dividing by value ({y}) close to 0" +/// ); /// } /// Ok(x / y) /// } /// ``` #[macro_export] macro_rules! bail { - ($msg:literal $(,)?) => { - return $crate::private::Err($crate::miette!($msg)); - }; ($err:expr $(,)?) => { return $crate::private::Err($crate::miette!($err)); }; - ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { - return $crate::private::Err($crate::miette!($fmt, $($key = $value),*)); + ($($key:ident = $value:expr),+; $($fmt:tt)+) => { + return $crate::private::Err( + $crate::miette!($($key = $value),+; $($fmt)+) + ); }; - ($fmt:expr, $($arg:tt)*) => { - return $crate::private::Err($crate::miette!($fmt, $($arg)*)); + ($($fmt:tt)+) => { + return $crate::private::Err($crate::miette!($($fmt)+)); }; } @@ -126,8 +128,8 @@ macro_rules! bail { /// fn divide(x: f64, y: f64) -> Result { /// ensure!( /// y.abs() >= 1e-3, -/// "dividing by value close to 0", -/// severity = Severity::Warning +/// severity = Severity::Warning; +/// "dividing by value ({y}) close to 0" /// ); /// Ok(x / y) /// } @@ -144,9 +146,11 @@ macro_rules! ensure { return $crate::private::Err($crate::miette!($err)); } }; - ($cond:expr, $fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { + ($cond:expr, $($key:ident = $value:expr),+; $($fmt:tt)+) => { if !$cond { - return $crate::private::Err($crate::miette!($fmt, $($key = $value),*)); + return $crate::private::Err( + $crate::miette!($($key = $value),+; $($fmt)+) + ); } }; ($cond:expr, $fmt:expr, $($arg:tt)*) => { @@ -162,33 +166,32 @@ macro_rules! ensure { /// /// With string literal and interpolation: /// ``` -/// # type V = (); -/// # -/// use miette::{miette, Result}; -/// -/// fn lookup(key: &str) -> Result { -/// if key.len() != 16 { -/// return Err(miette!("key length must be 16 characters, got {:?}", key)); -/// } +/// # use miette::miette; +/// let x = 1; +/// let y = 2; +/// let report = miette!("{x} + {} = {z}", y, z = x + y); +/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); /// -/// // ... -/// # Ok(()) -/// } +/// let report = miette!("{x} + {y} = {x + y}"); +/// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); /// ``` /// -/// With [`MietteDiagnostic`]-like arguments: +/// With [`diagnostic!`]-like arguments: /// ``` /// use miette::{miette, LabeledSpan, Severity}; /// /// let source = "(2 + 2".to_string(); /// let report = miette!( -/// "expected closing ')'", /// // 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" +/// url = "https://example.com"; // <- semicolon separates fields from message +/// +/// // Rest of the arguments are passed to `format!` +/// // to form diagnostic message +/// "expected closing ')'" /// ) /// .with_source_code(source); /// ``` @@ -198,21 +201,18 @@ macro_rules! ensure { /// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`. #[macro_export] macro_rules! miette { - ($msg:literal $(,)?) => { - // Handle $:literal as a special case to make cargo-expanded code more - // concise in the common case. - $crate::private::new_adhoc($msg) - }; ($err:expr $(,)?) => ({ use $crate::private::kind::*; let error = $err; (&error).miette_kind().new(error) }); - ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => { - $crate::Report::from($crate::diagnostic!($fmt, $($key = $value,)*)) + ($($key:ident = $value:expr),+; $($fmt:tt)+) => { + $crate::Report::from( + $crate::diagnostic!($($key = $value),+; $($fmt)+) + ) }; - ($fmt:expr, $($arg:tt)*) => { - $crate::private::new_adhoc(format!($fmt, $($arg)*)) + ($($fmt:tt)+) => { + $crate::private::new_adhoc(format!($($fmt)+)) }; } @@ -224,20 +224,35 @@ macro_rules! miette { /// /// let source = "(2 + 2".to_string(); /// let diag = diagnostic!( -/// "expected closing ')'", /// // 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" +/// url = "https://example.com"; // <- semicolon separates fields from message +/// +/// // 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; +/// +/// let diag = diagnostic!("{x} + {} = {z}", y, z = x + y); +/// assert_eq!(diag.message, "1 + 2 = 3"); +/// ``` #[macro_export] macro_rules! diagnostic { - ($fmt:expr $(, $key:ident = $value:expr)* $(,)?) => {{ - let mut diag = $crate::MietteDiagnostic::new(format!("{}", $fmt)); + ($($key:ident = $value:expr),+; $($fmt:tt)+) => {{ + let mut diag = $crate::MietteDiagnostic::new(format!($($fmt)+)); $(diag.$key = Some($value.into());)* diag }}; + ($($fmt:tt)+) => {{ + $crate::MietteDiagnostic::new(format!($($fmt)+)) + }}; } From 55f41a6bdb8a2ef9a0d9a66a2235fcb867267e69 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 7 May 2023 11:37:37 +0800 Subject: [PATCH 24/30] Remove literal case from ensure --- src/eyreish/macros.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 85da9692..039e4dda 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -136,11 +136,6 @@ macro_rules! bail { /// ``` #[macro_export] macro_rules! ensure { - ($cond:expr, $msg:literal $(,)?) => { - if !$cond { - return $crate::private::Err($crate::miette!($msg)); - } - }; ($cond:expr, $err:expr $(,)?) => { if !$cond { return $crate::private::Err($crate::miette!($err)); From 3e622128e80d0cd6900e9612150c8545ca6771b5 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 12 May 2023 21:28:52 +0800 Subject: [PATCH 25/30] Fix macro --- src/eyreish/macros.rs | 61 +++++++++++++++++------------------------- src/lib.rs | 4 +-- tests/test_chain.rs | 2 +- tests/test_downcast.rs | 38 +++++++++++++++++++++----- 4 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index 039e4dda..d3abcf2e 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -55,7 +55,7 @@ /// fn divide(x: f64, y: f64) -> Result { /// if y.abs() < 1e-3 { /// bail!( -/// severity = Severity::Warning; +/// severity = Severity::Warning, /// "dividing by value ({y}) close to 0" /// ); /// } @@ -64,16 +64,13 @@ /// ``` #[macro_export] macro_rules! bail { - ($err:expr $(,)?) => { - return $crate::private::Err($crate::miette!($err)); - }; - ($($key:ident = $value:expr),+; $($fmt:tt)+) => { + ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { return $crate::private::Err( - $crate::miette!($($key = $value),+; $($fmt)+) + $crate::miette!($($key = $value,)* $fmt $($arg)*) ); }; - ($($fmt:tt)+) => { - return $crate::private::Err($crate::miette!($($fmt)+)); + ($err:expr $(,)?) => { + return $crate::private::Err($crate::miette!($err)); }; } @@ -128,7 +125,7 @@ macro_rules! bail { /// fn divide(x: f64, y: f64) -> Result { /// ensure!( /// y.abs() >= 1e-3, -/// severity = Severity::Warning; +/// severity = Severity::Warning, /// "dividing by value ({y}) close to 0" /// ); /// Ok(x / y) @@ -136,21 +133,16 @@ macro_rules! bail { /// ``` #[macro_export] macro_rules! ensure { - ($cond:expr, $err:expr $(,)?) => { - if !$cond { - return $crate::private::Err($crate::miette!($err)); - } - }; - ($cond:expr, $($key:ident = $value:expr),+; $($fmt:tt)+) => { + ($cond:expr, $($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { if !$cond { return $crate::private::Err( - $crate::miette!($($key = $value),+; $($fmt)+) + $crate::miette!($($key = $value,)* $fmt $($arg)*) ); } }; - ($cond:expr, $fmt:expr, $($arg:tt)*) => { + ($cond:expr, $err:expr $(,)?) => { if !$cond { - return $crate::private::Err($crate::miette!($fmt, $($arg)*)); + return $crate::private::Err($crate::miette!($err)); } }; } @@ -167,7 +159,8 @@ macro_rules! ensure { /// let report = miette!("{x} + {} = {z}", y, z = x + y); /// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); /// -/// let report = miette!("{x} + {y} = {x + y}"); +/// let z = x + y; +/// let report = miette!("{x} + {y} = {z}"); /// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); /// ``` /// @@ -182,8 +175,7 @@ macro_rules! ensure { /// code = "expected::rparen", /// help = "always close your parens", /// labels = vec![LabeledSpan::at_offset(6, "here")], -/// url = "https://example.com"; // <- semicolon separates fields from message -/// +/// url = "https://example.com", /// // Rest of the arguments are passed to `format!` /// // to form diagnostic message /// "expected closing ')'" @@ -196,19 +188,16 @@ macro_rules! ensure { /// You can just replace `use`s of the `anyhow!`/`eyre!` macros with `miette!`. #[macro_export] macro_rules! miette { + ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => { + $crate::Report::from( + $crate::diagnostic!($($key = $value,)* $fmt $($arg)*) + ) + }; ($err:expr $(,)?) => ({ use $crate::private::kind::*; let error = $err; (&error).miette_kind().new(error) }); - ($($key:ident = $value:expr),+; $($fmt:tt)+) => { - $crate::Report::from( - $crate::diagnostic!($($key = $value),+; $($fmt)+) - ) - }; - ($($fmt:tt)+) => { - $crate::private::new_adhoc(format!($($fmt)+)) - }; } /// Construct a [`MietteDiagnostic`] in more user-friendly way. @@ -224,8 +213,7 @@ macro_rules! miette { /// code = "expected::rparen", /// help = "always close your parens", /// labels = vec![LabeledSpan::at_offset(6, "here")], -/// url = "https://example.com"; // <- semicolon separates fields from message -/// +/// url = "https://example.com", /// // Rest of the arguments are passed to `format!` /// // to form diagnostic message /// "expected closing ')'", @@ -239,15 +227,16 @@ macro_rules! miette { /// /// let diag = diagnostic!("{x} + {} = {z}", y, z = x + y); /// assert_eq!(diag.message, "1 + 2 = 3"); +/// +/// let z = x + y; +/// let diag = diagnostic!("{x} + {y} = {z}"); +/// assert_eq!(diag.message, "1 + 2 = 3"); /// ``` #[macro_export] macro_rules! diagnostic { - ($($key:ident = $value:expr),+; $($fmt:tt)+) => {{ - let mut diag = $crate::MietteDiagnostic::new(format!($($fmt)+)); + ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {{ + let mut diag = $crate::MietteDiagnostic::new(format!($fmt $($arg)*)); $(diag.$key = Some($value.into());)* diag }}; - ($($fmt:tt)+) => {{ - $crate::MietteDiagnostic::new(format!($($fmt)+)) - }}; } diff --git a/src/lib.rs b/src/lib.rs index 0c64321e..d1fb3eae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -604,11 +604,11 @@ //! //! let source = "2 + 2 * 2 = 8".to_string(); //! let report = miette!( -//! "Wrong answer", //! labels = vec[ //! LabeledSpan::at(12..13, "this should be 6"), //! ], -//! help = "'*' has greater precedence than '+'" +//! help = "'*' has greater precedence than '+'", +//! "Wrong answer" //! ).with_source_code(source); //! println!("{:?}", report) //! ``` diff --git a/tests/test_chain.rs b/tests/test_chain.rs index 3fdd4cdb..29d3927b 100644 --- a/tests/test_chain.rs +++ b/tests/test_chain.rs @@ -1,7 +1,7 @@ use miette::{miette, 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] diff --git a/tests/test_downcast.rs b/tests/test_downcast.rs index 2823177e..cf5ab2ee 100644 --- a/tests/test_downcast.rs +++ b/tests/test_downcast.rs @@ -3,7 +3,7 @@ mod drop; use self::common::*; use self::drop::{DetectDrop, Flag}; -use miette::{Diagnostic, Report}; +use miette::{Diagnostic, MietteDiagnostic, Report}; use std::error::Error as StdError; use std::fmt::{self, Display}; use std::io; @@ -12,11 +12,19 @@ use std::io; fn test_downcast() { assert_eq!( "oh no!", - bail_literal().unwrap_err().downcast::<&str>().unwrap(), + bail_literal() + .unwrap_err() + .downcast::() + .unwrap() + .message, ); assert_eq!( "oh no!", - bail_fmt().unwrap_err().downcast::().unwrap(), + bail_fmt() + .unwrap_err() + .downcast::() + .unwrap() + .message, ); assert_eq!( "oh no!", @@ -32,11 +40,19 @@ fn test_downcast() { fn test_downcast_ref() { assert_eq!( "oh no!", - *bail_literal().unwrap_err().downcast_ref::<&str>().unwrap(), + bail_literal() + .unwrap_err() + .downcast_ref::() + .unwrap() + .message, ); assert_eq!( "oh no!", - bail_fmt().unwrap_err().downcast_ref::().unwrap(), + bail_fmt() + .unwrap_err() + .downcast_ref::() + .unwrap() + .message, ); assert_eq!( "oh no!", @@ -52,11 +68,19 @@ fn test_downcast_ref() { fn test_downcast_mut() { assert_eq!( "oh no!", - *bail_literal().unwrap_err().downcast_mut::<&str>().unwrap(), + bail_literal() + .unwrap_err() + .downcast_mut::() + .unwrap() + .message, ); assert_eq!( "oh no!", - bail_fmt().unwrap_err().downcast_mut::().unwrap(), + bail_fmt() + .unwrap_err() + .downcast_mut::() + .unwrap() + .message, ); assert_eq!( "oh no!", From 5e6795399e849e6296a426128b7c98ebe2742f87 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 12 May 2023 21:36:04 +0800 Subject: [PATCH 26/30] Use `mut self` in builder functions --- src/miette_diagnostic.rs | 64 +++++++++++++++------------------------- 1 file changed, 24 insertions(+), 40 deletions(-) diff --git a/src/miette_diagnostic.rs b/src/miette_diagnostic.rs index 8d2060e0..29a0c80d 100644 --- a/src/miette_diagnostic.rs +++ b/src/miette_diagnostic.rs @@ -104,11 +104,9 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Oops, something went wrong!"); /// assert_eq!(diag.code, Some("foo::bar::baz".to_string())); /// ``` - pub fn with_code(self, code: impl Into) -> Self { - Self { - code: Some(code.into()), - ..self - } + pub fn with_code(mut self, code: impl Into) -> Self { + self.code = Some(code.into()); + self } /// Return new diagnostic with the given severity. @@ -121,11 +119,9 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "I warn you to stop!"); /// assert_eq!(diag.severity, Some(Severity::Warning)); /// ``` - pub fn with_severity(self, severity: Severity) -> Self { - Self { - severity: Some(severity), - ..self - } + pub fn with_severity(mut self, severity: Severity) -> Self { + self.severity = Some(severity); + self } /// Return new diagnostic with the given help message. @@ -138,11 +134,9 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "PC is not working"); /// assert_eq!(diag.help, Some("Try to reboot it again".to_string())); /// ``` - pub fn with_help(self, help: impl Into) -> Self { - Self { - help: Some(help.into()), - ..self - } + pub fn with_help(mut self, help: impl Into) -> Self { + self.help = Some(help.into()); + self } /// Return new diagnostic with the given URL. @@ -159,11 +153,9 @@ impl MietteDiagnostic { /// Some("https://letmegooglethat.com/?q=Why+my+pc+doesn%27t+work".to_string()) /// ); /// ``` - pub fn with_url(self, url: impl Into) -> Self { - Self { - url: Some(url.into()), - ..self - } + pub fn with_url(mut self, url: impl Into) -> Self { + self.url = Some(url.into()); + self } /// Return new diagnostic with the given label. @@ -181,11 +173,9 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Wrong best language"); /// assert_eq!(diag.labels, Some(vec![label])); /// ``` - pub fn with_label(self, label: impl Into) -> Self { - Self { - labels: Some(vec![label.into()]), - ..self - } + pub fn with_label(mut self, label: impl Into) -> Self { + self.labels = Some(vec![label.into()]); + self } /// Return new diagnostic with the given labels. @@ -206,11 +196,9 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Typos in 'hello world'"); /// assert_eq!(diag.labels, Some(labels)); /// ``` - pub fn with_labels(self, labels: impl IntoIterator) -> Self { - Self { - labels: Some(labels.into_iter().collect()), - ..self - } + pub fn with_labels(mut self, labels: impl IntoIterator) -> Self { + self.labels = Some(labels.into_iter().collect()); + self } /// Return new diagnostic with new label added to the existing ones. @@ -229,13 +217,11 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Typos in 'hello world'"); /// assert_eq!(diag.labels, Some(vec![label1, label2])); /// ``` - pub fn and_label(self, label: impl Into) -> Self { + pub fn and_label(mut self, label: impl Into) -> Self { let mut labels = self.labels.unwrap_or_default(); labels.push(label.into()); - Self { - labels: Some(labels), - ..self - } + self.labels = Some(labels); + self } /// Return new diagnostic with new labels added to the existing ones. @@ -255,12 +241,10 @@ impl MietteDiagnostic { /// assert_eq!(diag.message, "Typos in 'hello world!'"); /// assert_eq!(diag.labels, Some(vec![label1, label2, label3])); /// ``` - pub fn and_labels(self, labels: impl IntoIterator) -> Self { + pub fn and_labels(mut self, labels: impl IntoIterator) -> Self { let mut all_labels = self.labels.unwrap_or_default(); all_labels.extend(labels.into_iter()); - Self { - labels: Some(all_labels), - ..self - } + self.labels = Some(all_labels); + self } } From 5c94dd08fc3bff9a5b4bac5c4ae3bd96cea08d0d Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 12 May 2023 22:07:36 +0800 Subject: [PATCH 27/30] Sync README.md --- README.md | 67 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c12734d0..ff25b3ab 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ You run miette? You run her code like the software? Oh. Oh! Error code for coder! Error code for One Thousand Lines! -### About +## About `miette` is a diagnostic library for Rust. It includes a series of 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 libraries and such might not want. -### Table of Contents +## Table of Contents - [About](#about) - [Features](#features) @@ -47,10 +47,11 @@ libraries and such might not want. - [... multiple related errors](#-multiple-related-errors) - [... delayed source code](#-delayed-source-code) - [... handler options](#-handler-options) + - [... dynamic diagnostics](#-dynamic-diagnostics) - [Acknowledgements](#acknowledgements) - [License](#license) -### Features +## Features - Generic [`Diagnostic`] protocol, compatible (and dependent on) [`std::error::Error`]. @@ -75,7 +76,7 @@ the following features: - Cause chain printing - Turns diagnostic codes into links in [supported terminals](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda). -### Installing +## Installing ```sh $ cargo add miette @@ -87,7 +88,7 @@ If you want to use the fancy printer in all these screenshots: $ cargo add miette --features fancy ``` -### Example +## Example ```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 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 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 using something like [`Report`] in a library. -#### ... in application code +### ... 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 @@ -247,11 +248,11 @@ pub fn some_tool() -> Result { } ``` -To construct your own simple adhoc error use the `miette::miette!` macro: +To construct your own simple adhoc error use the [`miette!`] macro: ```rust // my_app/lib/my_internal_file.rs -use miette::{IntoDiagnostic, Result, WrapErr, miette}; +use miette::{miette, IntoDiagnostic, Result, WrapErr}; use semver::Version; pub fn some_tool() -> Result { @@ -262,9 +263,7 @@ pub fn some_tool() -> Result { } ``` -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 `Result` as your return value, and it will pretty-print your diagnostics @@ -294,7 +293,7 @@ enabled: miette = { version = "X.Y.Z", features = ["fancy"] } ``` -#### ... diagnostic code URLs +### ... diagnostic code URLs `miette` supports providing a URL for individual diagnostics. This URL will be displayed as an actual link in supported terminals, like so: @@ -347,7 +346,7 @@ use thiserror::Error; struct MyErr; ``` -#### ... snippets +### ... snippets Along with its general error handling and reporting features, `miette` also 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: 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 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. 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 from `thiserror`, and implements a method for @@ -568,7 +566,7 @@ struct MyError { struct OtherError; ``` -#### ... handler options +### ... handler options [`MietteHandler`] is the default handler, and is very customizable. In most cases, you can simply use [`MietteHandlerOpts`] to tweak its behavior @@ -587,13 +585,32 @@ miette::set_hook(Box::new(|_| { .build(), ) })) - ``` See the docs for [`MietteHandlerOpts`] for more details on what you can 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 other projects and their authors: @@ -612,7 +629,7 @@ other projects and their authors: - [`ariadne`](https://crates.io/crates/ariadne) for pushing forward how _pretty_ these diagnostics can really look! -### License +## License `miette` is released to the Rust community under the [Apache 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. [`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 [`Diagnostic`]: https://docs.rs/miette/latest/miette/trait.Diagnostic.html [`IntoDiagnostic`]: https://docs.rs/miette/latest/miette/trait.IntoDiagnostic.html [`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.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 [`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html [`Result`]: https://docs.rs/miette/latest/miette/type.Result.html From 253a0f8a3235ca9170ebc95dfec285c6c7876f41 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Fri, 12 May 2023 22:13:07 +0800 Subject: [PATCH 28/30] Update .tpl --- README.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.tpl b/README.tpl index a7913449..d598eb8b 100644 --- a/README.tpl +++ b/README.tpl @@ -4,11 +4,13 @@ {{readme}} [`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 -[`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 [`MietteHandlerOpts`]: https://docs.rs/miette/latest/miette/struct.MietteHandlerOpts.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 [`ReportHandler`]: https://docs.rs/miette/latest/miette/struct.ReportHandler.html [`Result`]: https://docs.rs/miette/latest/miette/type.Result.html From 4520d66815d84b737b2710843ed4f39c44be0be4 Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sat, 13 May 2023 09:31:00 +0800 Subject: [PATCH 29/30] Fix clippy --- src/protocol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protocol.rs b/src/protocol.rs index fd5f7b12..0f606f65 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -256,7 +256,7 @@ impl LabeledSpan { /// ) /// ``` pub fn at_offset(offset: ByteOffset, label: impl Into) -> Self { - Self::new(Some(label.into()), offset.into(), 0) + Self::new(Some(label.into()), offset, 0) } /// Makes a new label without text, that underlines a specific span. From 7fb4cb24943ea25157f5904730a8097ddf12a7cd Mon Sep 17 00:00:00 2001 From: Gavrilikhin Daniil Date: Sun, 14 May 2023 00:18:56 +0800 Subject: [PATCH 30/30] Add and use `no-format-args-capture` flag --- .github/workflows/ci.yml | 4 +++ Cargo.toml | 11 ++++++- src/eyreish/macros.rs | 64 +++++++++++++++++++++++++++++++++++----- src/lib.rs | 2 +- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 710eb1c8..4fafac33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,11 @@ jobs: - name: Clippy run: cargo clippy --all -- -D warnings - name: Run tests + if: matrix.rust == 'stable' 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: name: Miri diff --git a/Cargo.toml b/Cargo.toml index 7615d6b7..61d357af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,16 @@ lazy_static = "1.4" [features] 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"] [workspace] diff --git a/src/eyreish/macros.rs b/src/eyreish/macros.rs index d3abcf2e..50605e71 100644 --- a/src/eyreish/macros.rs +++ b/src/eyreish/macros.rs @@ -16,7 +16,14 @@ /// # let resource = 0; /// # /// 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(()) /// # } @@ -56,7 +63,14 @@ /// if y.abs() < 1e-3 { /// bail!( /// severity = Severity::Warning, -/// "dividing by value ({y}) close to 0" +#[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) @@ -126,7 +140,14 @@ macro_rules! bail { /// ensure!( /// y.abs() >= 1e-3, /// severity = Severity::Warning, -/// "dividing by value ({y}) close to 0" +#[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) /// } @@ -156,11 +177,26 @@ macro_rules! ensure { /// # use miette::miette; /// let x = 1; /// let y = 2; -/// let report = miette!("{x} + {} = {z}", y, z = x + y); +#[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);"# +)] +/// /// assert_eq!(report.to_string().as_str(), "1 + 2 = 3"); /// /// let z = x + y; -/// let report = miette!("{x} + {y} = {z}"); +#[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"); /// ``` /// @@ -225,11 +261,25 @@ macro_rules! miette { /// let x = 1; /// let y = 2; /// -/// let diag = diagnostic!("{x} + {} = {z}", y, z = x + y); +#[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; -/// let diag = diagnostic!("{x} + {y} = {z}"); +#[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] diff --git a/src/lib.rs b/src/lib.rs index d1fb3eae..cc113ce3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -599,7 +599,7 @@ //! then you may want to use [`miette!`], [`diagnostic!`] macros or //! [`MietteDiagnostic`] directly to create diagnostic on the fly. //! -//! ```rs +//! ```rust,ignore //! # use miette::{miette, LabeledSpan, Report}; //! //! let source = "2 + 2 * 2 = 8".to_string();