Skip to main content

error_enum_core/
lib.rs

1//! # `error-enum-core`
2//!
3//! A core crate for [`error-enum`](https://crates.io/crates/error-enum) providing
4//! traits and implementations for error enums with rich diagnostics support.
5//!
6//! Please refer to [`error-enum`](https://crates.io/crates/error-enum) and
7//! [`its documentation`](https://docs.rs/error-enum/) for more details.
8#![warn(unused_crate_dependencies)]
9#![no_std]
10#![cfg_attr(docsrs, feature(doc_cfg))]
11
12pub use alloc::{boxed::Box, format, string::String};
13use core::fmt;
14pub use indexer::{Indexer, LineIndexer};
15pub use span::{SimpleSpan, Span};
16
17extern crate alloc;
18#[cfg(feature = "std")]
19extern crate std;
20
21mod indexer;
22mod span;
23
24#[cfg(feature = "annotate-snippets")]
25mod annotate_snippets_impl;
26#[cfg(feature = "ariadne")]
27mod ariadne_impl;
28#[cfg(feature = "codespan-reporting")]
29mod codespan_reporting_impl;
30#[cfg(feature = "miette")]
31mod miette_impl;
32
33/// Enum representing the kind of an error.
34#[derive(Clone, Copy, Default)]
35pub enum Kind {
36    /// Error kind.
37    #[default]
38    Error,
39    /// Warning kind.
40    Warn,
41}
42
43impl Kind {
44    /// Get short representation of the [Kind].
45    pub fn short_str(&self) -> &'static str {
46        match self {
47            Kind::Error => "E",
48            Kind::Warn => "W",
49        }
50    }
51}
52
53/// Trait for error types generated by [`error_type!`] macro and [`ErrorType`] derive macro.
54///
55/// For conversion to other diagnostic types, see [`ErrorTypeExt`].
56///
57/// [`error_type!`]: https://docs.rs/error-enum-macros/latest/error_enum_macros/macro.error_type.html
58/// [`ErrorType`]: https://docs.rs/error-enum-macros/latest/error_enum_macros/derive.ErrorType.html
59pub trait ErrorType: core::error::Error {
60    /// The span type associated with the error type.
61    type Span: Span + Default;
62    /// The message type associated with the error type.
63    type Message: fmt::Display;
64
65    /// Get the kind of the error.
66    fn kind(&self) -> Kind;
67    /// Get the number of the error.
68    fn number(&self) -> &str;
69    /// Get the code of the error.
70    ///
71    /// Normally the code is a combination of kind short string and number,
72    /// like "E0", "W1", etc.
73    fn code(&self) -> &str;
74    /// Get the primary span of the error.
75    fn primary_span(&self) -> Option<Self::Span>;
76    /// Get the primary message of the error.
77    fn primary_message(&self) -> Self::Message;
78    /// Get the primary label of the error.
79    fn primary_label(&self) -> Self::Message;
80
81    /// Get the primary diagnostic of the error.
82    fn primary(&self) -> (Option<Self::Span>, Self::Message, Self::Message) {
83        (
84            self.primary_span(),
85            self.primary_message(),
86            self.primary_label(),
87        )
88    }
89
90    /// Get additional spans, messages, and labels of the error.
91    fn additional(
92        &self,
93    ) -> Box<dyn Iterator<Item = (Option<Self::Span>, Self::Message, Self::Message)>>;
94}
95
96impl<T: ErrorType + ?Sized> ErrorType for &T {
97    type Span = T::Span;
98    type Message = T::Message;
99
100    #[inline]
101    fn kind(&self) -> Kind {
102        (*self).kind()
103    }
104    #[inline]
105    fn number(&self) -> &str {
106        (*self).number()
107    }
108    #[inline]
109    fn code(&self) -> &str {
110        (*self).code()
111    }
112    #[inline]
113    fn primary_span(&self) -> Option<Self::Span> {
114        (*self).primary_span()
115    }
116    #[inline]
117    fn primary_message(&self) -> Self::Message {
118        (*self).primary_message()
119    }
120    #[inline]
121    fn primary_label(&self) -> Self::Message {
122        (*self).primary_label()
123    }
124
125    #[inline]
126    fn primary(&self) -> (Option<Self::Span>, Self::Message, Self::Message) {
127        (*self).primary()
128    }
129
130    #[inline]
131    fn additional(
132        &self,
133    ) -> Box<dyn Iterator<Item = (Option<Self::Span>, Self::Message, Self::Message)>> {
134        (*self).additional()
135    }
136}
137
138/// Conversion to other diagnostic types.
139pub trait ErrorTypeExt: ErrorType {
140    /// Format the error as an [annotate snippet].
141    ///
142    /// [annotate snippet]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/snippet/struct.Snippet.html
143    #[cfg(feature = "annotate-snippets")]
144    #[cfg_attr(docsrs, doc(cfg(feature = "annotate-snippets")))]
145    fn fmt_as_annotate_snippets(&self) -> String {
146        annotate_snippets_impl::fmt_as_annotate_snippets(
147            self,
148            annotate_snippets::display_list::FormatOptions::default(),
149        )
150    }
151    /// Format the error as an [annotate snippet] with [format options].
152    ///
153    /// [annotate snippet]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/snippet/struct.Snippet.html
154    /// [format options]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/display_list/struct.FormatOptions.html
155    #[cfg(feature = "annotate-snippets")]
156    #[cfg_attr(docsrs, doc(cfg(feature = "annotate-snippets")))]
157    fn fmt_as_annotate_snippets_with_opts(
158        &self,
159        opts: annotate_snippets::display_list::FormatOptions,
160    ) -> String {
161        annotate_snippets_impl::fmt_as_annotate_snippets(self, opts)
162    }
163
164    /// Format the error as an [Ariadne report].
165    ///
166    /// [Ariadne report]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Report.html
167    #[cfg(feature = "ariadne")]
168    #[cfg_attr(docsrs, doc(cfg(feature = "ariadne")))]
169    fn fmt_as_ariadne_report(&self) -> Result<String, std::io::Error> {
170        ariadne_impl::fmt_as_ariadne_report(
171            self,
172            ariadne::Config::new().with_index_type(ariadne::IndexType::Byte),
173        )
174    }
175    /// Format the error as an [Ariadne report] with [Ariadne config].
176    ///
177    /// [Ariadne report]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Report.html
178    /// [Ariadne config]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Config.html
179    #[cfg(feature = "ariadne")]
180    #[cfg_attr(docsrs, doc(cfg(feature = "ariadne")))]
181    fn fmt_as_ariadne_report_with(
182        &self,
183        config: ariadne::Config,
184    ) -> Result<String, std::io::Error> {
185        ariadne_impl::fmt_as_ariadne_report(self, config)
186    }
187
188    /// Format the error as an [Codespan diagnostic].
189    ///
190    /// [Codespan diagnostic]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/diagnostic/struct.Diagnostic.html
191    /// [Codespan config]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/term/config/struct.Config.html
192    #[cfg(feature = "codespan-reporting")]
193    #[cfg_attr(docsrs, doc(cfg(feature = "codespan-reporting")))]
194    fn as_codespan_diagnostic(
195        &self,
196    ) -> (
197        codespan_reporting::diagnostic::Diagnostic<usize>,
198        codespan_reporting_impl::Files<Self>,
199    ) {
200        codespan_reporting_impl::to_codespan_diagnostic(self)
201    }
202    /// Format the error as an [Codespan diagnostic] with [Codespan config].
203    ///
204    /// [Codespan diagnostic]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/diagnostic/struct.Diagnostic.html
205    /// [Codespan config]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/term/config/struct.Config.html
206    #[cfg(feature = "codespan-reporting")]
207    #[cfg_attr(docsrs, doc(cfg(feature = "codespan-reporting")))]
208    fn fmt_as_codespan_diagnostic_with(
209        &self,
210        config: codespan_reporting::term::Config,
211        styles: Option<&codespan_reporting::term::Styles>,
212    ) -> Result<String, codespan_reporting::files::Error> {
213        codespan_reporting_impl::fmt_as_codespan_diagnostic(self, config, styles)
214    }
215
216    /// Convert the error to a [Miette diagnostic].
217    ///
218    /// [Miette diagnostic]: https://docs.rs/miette/7.6.0/miette/trait.Diagnostic.html
219    #[cfg(feature = "miette")]
220    #[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
221    fn as_miette_diagnostic(&self) -> impl miette::Diagnostic + '_
222    where
223        Self::Span: Send + Sync,
224    {
225        miette_impl::Wrapper::new(self)
226    }
227    /// Format the error as a [Miette diagnostic] with a [Miette handler].
228    ///
229    /// [Miette diagnostic]: https://docs.rs/miette/7.6.0/miette/trait.Diagnostic.html
230    /// [Miette Handler]: https://docs.rs/miette/7.6.0/miette/trait.ReportHandler.html
231    #[cfg(feature = "miette")]
232    #[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
233    fn fmt_as_miette_diagnostic_with(&self, handler: &impl miette::ReportHandler) -> String
234    where
235        Self: 'static + Sized,
236        Self::Span: Send + Sync,
237    {
238        miette_impl::Wrapper::new(self).fmt_with(handler)
239    }
240}
241
242impl<T: ErrorType + ?Sized> ErrorTypeExt for T {}