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::{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/// [`error_type!`]: https://docs.rs/error-enum-macros/latest/error_enum_macros/macro.error_type.html
56/// [`ErrorType`]: https://docs.rs/error-enum-macros/latest/error_enum_macros/derive.ErrorType.html
57pub trait ErrorType: core::error::Error {
58    /// The span type associated with the error type.
59    type Span: Span + Default;
60    /// The message type associated with the error type.
61    type Message: fmt::Display;
62
63    /// Get the kind of the error.
64    fn kind(&self) -> Kind;
65    /// Get the number of the error.
66    fn number(&self) -> &str;
67    /// Get the code of the error.
68    ///
69    /// Normally the code is a combination of kind short string and number,
70    /// like "E0", "W1", etc.
71    fn code(&self) -> &str;
72    /// Get the primary span of the error.
73    fn primary_span(&self) -> Option<Self::Span>;
74    /// Get the primary message of the error.
75    fn primary_message(&self) -> Self::Message;
76    /// Get the primary label of the error.
77    fn primary_label(&self) -> Self::Message;
78
79    /// Get the primary diagnostic of the error.
80    fn primary(&self) -> (Option<Self::Span>, Self::Message, Self::Message) {
81        (
82            self.primary_span(),
83            self.primary_message(),
84            self.primary_label(),
85        )
86    }
87
88    /// Get additional spans, messages, and labels of the error.
89    fn additional(
90        &self,
91    ) -> impl Iterator<Item = (Option<Self::Span>, Self::Message, Self::Message)> + '_;
92
93    /// Format the error as an [annotate snippet].
94    ///
95    /// [annotate snippet]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/snippet/struct.Snippet.html
96    #[cfg(feature = "annotate-snippets")]
97    #[cfg_attr(docsrs, doc(cfg(feature = "annotate-snippets")))]
98    fn fmt_as_annotate_snippets(&self) -> String {
99        annotate_snippets_impl::fmt_as_annotate_snippets(
100            self,
101            annotate_snippets::display_list::FormatOptions::default(),
102        )
103    }
104    /// Format the error as an [annotate snippet] with [format options].
105    ///
106    /// [annotate snippet]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/snippet/struct.Snippet.html
107    /// [format options]: https://docs.rs/annotate-snippets/0.9.1/annotate_snippets/display_list/struct.FormatOptions.html
108    #[cfg(feature = "annotate-snippets")]
109    #[cfg_attr(docsrs, doc(cfg(feature = "annotate-snippets")))]
110    fn fmt_as_annotate_snippets_with_opts(
111        &self,
112        opts: annotate_snippets::display_list::FormatOptions,
113    ) -> String {
114        annotate_snippets_impl::fmt_as_annotate_snippets(self, opts)
115    }
116
117    /// Format the error as an [Ariadne report].
118    ///
119    /// [Ariadne report]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Report.html
120    #[cfg(feature = "ariadne")]
121    #[cfg_attr(docsrs, doc(cfg(feature = "ariadne")))]
122    fn fmt_as_ariadne_report(&self) -> Result<String, std::io::Error> {
123        ariadne_impl::fmt_as_ariadne_report(
124            self,
125            ariadne::Config::new().with_index_type(ariadne::IndexType::Byte),
126        )
127    }
128    /// Format the error as an [Ariadne report] with [Ariadne config].
129    ///
130    /// [Ariadne report]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Report.html
131    /// [Ariadne config]: https://docs.rs/ariadne/0.6.0/ariadne/struct.Config.html
132    #[cfg(feature = "ariadne")]
133    #[cfg_attr(docsrs, doc(cfg(feature = "ariadne")))]
134    fn fmt_as_ariadne_report_with(
135        &self,
136        config: ariadne::Config,
137    ) -> Result<String, std::io::Error> {
138        ariadne_impl::fmt_as_ariadne_report(self, config)
139    }
140
141    /// Format the error as an [Codespan diagnostic].
142    ///
143    /// [Codespan diagnostic]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/diagnostic/struct.Diagnostic.html
144    /// [Codespan config]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/term/config/struct.Config.html
145    #[cfg(feature = "codespan-reporting")]
146    #[cfg_attr(docsrs, doc(cfg(feature = "codespan-reporting")))]
147    fn as_codespan_diagnostic(
148        &self,
149    ) -> (
150        codespan_reporting::diagnostic::Diagnostic<usize>,
151        codespan_reporting_impl::Files<Self>,
152    ) {
153        codespan_reporting_impl::to_codespan_diagnostic(self)
154    }
155    /// Format the error as an [Codespan diagnostic] with [Codespan config].
156    ///
157    /// [Codespan diagnostic]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/diagnostic/struct.Diagnostic.html
158    /// [Codespan config]: https://docs.rs/codespan-reporting/0.13.1/codespan_reporting/term/config/struct.Config.html
159    #[cfg(feature = "codespan-reporting")]
160    #[cfg_attr(docsrs, doc(cfg(feature = "codespan-reporting")))]
161    fn fmt_as_codespan_diagnostic_with(
162        &self,
163        config: codespan_reporting::term::Config,
164        styles: Option<&codespan_reporting::term::Styles>,
165    ) -> Result<String, codespan_reporting::files::Error> {
166        codespan_reporting_impl::fmt_as_codespan_diagnostic(self, config, styles)
167    }
168
169    /// Convert the error to a [Miette diagnostic].
170    ///
171    /// [Miette diagnostic]: https://docs.rs/miette/7.6.0/miette/trait.Diagnostic.html
172    #[cfg(feature = "miette")]
173    #[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
174    fn as_miette_diagnostic(&self) -> impl miette::Diagnostic + '_
175    where
176        Self::Span: Send + Sync,
177    {
178        miette_impl::Wrapper::new(self)
179    }
180    /// Format the error as a [Miette diagnostic] with a [Miette handler].
181    ///
182    /// [Miette diagnostic]: https://docs.rs/miette/7.6.0/miette/trait.Diagnostic.html
183    /// [Miette Handler]: https://docs.rs/miette/7.6.0/miette/trait.ReportHandler.html
184    #[cfg(feature = "miette")]
185    #[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
186    fn fmt_as_miette_diagnostic_with(&self, handler: &impl miette::ReportHandler) -> String
187    where
188        Self: 'static + Sized,
189        Self::Span: Send + Sync,
190    {
191        miette_impl::Wrapper::new(self).fmt_with(handler)
192    }
193}
194
195impl<T: ErrorType + ?Sized> ErrorType for &T {
196    type Span = T::Span;
197    type Message = T::Message;
198
199    #[inline]
200    fn kind(&self) -> Kind {
201        (*self).kind()
202    }
203    #[inline]
204    fn number(&self) -> &str {
205        (*self).number()
206    }
207    #[inline]
208    fn code(&self) -> &str {
209        (*self).code()
210    }
211    #[inline]
212    fn primary_span(&self) -> Option<Self::Span> {
213        (*self).primary_span()
214    }
215    #[inline]
216    fn primary_message(&self) -> Self::Message {
217        (*self).primary_message()
218    }
219    #[inline]
220    fn primary_label(&self) -> Self::Message {
221        (*self).primary_label()
222    }
223
224    #[inline]
225    fn primary(&self) -> (Option<Self::Span>, Self::Message, Self::Message) {
226        (*self).primary()
227    }
228
229    #[inline]
230    fn additional(
231        &self,
232    ) -> impl Iterator<Item = (Option<Self::Span>, Self::Message, Self::Message)> + '_ {
233        (*self).additional()
234    }
235
236    #[cfg(feature = "annotate-snippets")]
237    #[inline]
238    fn fmt_as_annotate_snippets(&self) -> String {
239        (*self).fmt_as_annotate_snippets()
240    }
241    #[cfg(feature = "annotate-snippets")]
242    #[inline]
243    fn fmt_as_annotate_snippets_with_opts(
244        &self,
245        opts: annotate_snippets::display_list::FormatOptions,
246    ) -> String {
247        (*self).fmt_as_annotate_snippets_with_opts(opts)
248    }
249
250    #[cfg(feature = "ariadne")]
251    #[inline]
252    fn fmt_as_ariadne_report(&self) -> Result<String, std::io::Error> {
253        (*self).fmt_as_ariadne_report()
254    }
255    #[cfg(feature = "ariadne")]
256    #[inline]
257    fn fmt_as_ariadne_report_with(
258        &self,
259        config: ariadne::Config,
260    ) -> Result<String, std::io::Error> {
261        (*self).fmt_as_ariadne_report_with(config)
262    }
263
264    #[cfg(feature = "codespan-reporting")]
265    #[inline]
266    fn as_codespan_diagnostic(
267        &self,
268    ) -> (
269        codespan_reporting::diagnostic::Diagnostic<usize>,
270        codespan_reporting_impl::Files<Self>,
271    ) {
272        (*self).as_codespan_diagnostic()
273    }
274    #[cfg(feature = "codespan-reporting")]
275    #[inline]
276    fn fmt_as_codespan_diagnostic_with(
277        &self,
278        config: codespan_reporting::term::Config,
279        styles: Option<&codespan_reporting::term::Styles>,
280    ) -> Result<String, codespan_reporting::files::Error> {
281        (*self).fmt_as_codespan_diagnostic_with(config, styles)
282    }
283
284    #[cfg(feature = "miette")]
285    #[inline]
286    fn as_miette_diagnostic(&self) -> impl miette::Diagnostic + '_
287    where
288        Self::Span: Send + Sync,
289    {
290        (*self).as_miette_diagnostic()
291    }
292}