• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*!
2 This module defines the core of the miette protocol: a series of types and
3 traits that you can implement to get access to miette's (and related library's)
4 full reporting and such features.
5 */
6 use std::{
7     fmt::{self, Display},
8     fs,
9     panic::Location,
10 };
11 
12 #[cfg(feature = "serde")]
13 use serde::{Deserialize, Serialize};
14 
15 use crate::MietteError;
16 
17 /// Adds rich metadata to your Error that can be used by
18 /// [`Report`](crate::Report) to print really nice and human-friendly error
19 /// messages.
20 pub trait Diagnostic: std::error::Error {
21     /// Unique diagnostic code that can be used to look up more information
22     /// about this `Diagnostic`. Ideally also globally unique, and documented
23     /// in the toplevel crate's documentation for easy searching. Rust path
24     /// format (`foo::bar::baz`) is recommended, but more classic codes like
25     /// `E0123` or enums will work just fine.
code<'a>(&'a self) -> Option<Box<dyn Display + 'a>>26     fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
27         None
28     }
29 
30     /// Diagnostic severity. This may be used by
31     /// [`ReportHandler`](crate::ReportHandler)s to change the display format
32     /// of this diagnostic.
33     ///
34     /// If `None`, reporters should treat this as [`Severity::Error`].
severity(&self) -> Option<Severity>35     fn severity(&self) -> Option<Severity> {
36         None
37     }
38 
39     /// Additional help text related to this `Diagnostic`. Do you have any
40     /// advice for the poor soul who's just run into this issue?
help<'a>(&'a self) -> Option<Box<dyn Display + 'a>>41     fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
42         None
43     }
44 
45     /// URL to visit for a more detailed explanation/help about this
46     /// `Diagnostic`.
url<'a>(&'a self) -> Option<Box<dyn Display + 'a>>47     fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
48         None
49     }
50 
51     /// Source code to apply this `Diagnostic`'s [`Diagnostic::labels`] to.
source_code(&self) -> Option<&dyn SourceCode>52     fn source_code(&self) -> Option<&dyn SourceCode> {
53         None
54     }
55 
56     /// Labels to apply to this `Diagnostic`'s [`Diagnostic::source_code`]
labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>>57     fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
58         None
59     }
60 
61     /// Additional related `Diagnostic`s.
related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>>62     fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
63         None
64     }
65 
66     /// The cause of the error.
diagnostic_source(&self) -> Option<&dyn Diagnostic>67     fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
68         None
69     }
70 }
71 
72 macro_rules! box_impls {
73     ($($box_type:ty),*) => {
74         $(
75             impl std::error::Error for $box_type {
76                 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
77                     (**self).source()
78                 }
79 
80                 fn cause(&self) -> Option<&dyn std::error::Error> {
81                     self.source()
82                 }
83             }
84         )*
85     }
86 }
87 
88 box_impls! {
89     Box<dyn Diagnostic>,
90     Box<dyn Diagnostic + Send>,
91     Box<dyn Diagnostic + Send + Sync>
92 }
93 
94 impl<T: Diagnostic + Send + Sync + 'static> From<T>
95     for Box<dyn Diagnostic + Send + Sync + 'static>
96 {
from(diag: T) -> Self97     fn from(diag: T) -> Self {
98         Box::new(diag)
99     }
100 }
101 
102 impl<T: Diagnostic + Send + Sync + 'static> From<T> for Box<dyn Diagnostic + Send + 'static> {
from(diag: T) -> Self103     fn from(diag: T) -> Self {
104         Box::<dyn Diagnostic + Send + Sync>::from(diag)
105     }
106 }
107 
108 impl<T: Diagnostic + Send + Sync + 'static> From<T> for Box<dyn Diagnostic + 'static> {
from(diag: T) -> Self109     fn from(diag: T) -> Self {
110         Box::<dyn Diagnostic + Send + Sync>::from(diag)
111     }
112 }
113 
114 impl From<&str> for Box<dyn Diagnostic> {
from(s: &str) -> Self115     fn from(s: &str) -> Self {
116         From::from(String::from(s))
117     }
118 }
119 
120 impl<'a> From<&str> for Box<dyn Diagnostic + Send + Sync + 'a> {
from(s: &str) -> Self121     fn from(s: &str) -> Self {
122         From::from(String::from(s))
123     }
124 }
125 
126 impl From<String> for Box<dyn Diagnostic> {
from(s: String) -> Self127     fn from(s: String) -> Self {
128         let err1: Box<dyn Diagnostic + Send + Sync> = From::from(s);
129         let err2: Box<dyn Diagnostic> = err1;
130         err2
131     }
132 }
133 
134 impl From<String> for Box<dyn Diagnostic + Send + Sync> {
from(s: String) -> Self135     fn from(s: String) -> Self {
136         struct StringError(String);
137 
138         impl std::error::Error for StringError {}
139         impl Diagnostic for StringError {}
140 
141         impl Display for StringError {
142             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143                 Display::fmt(&self.0, f)
144             }
145         }
146 
147         // Purposefully skip printing "StringError(..)"
148         impl fmt::Debug for StringError {
149             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150                 fmt::Debug::fmt(&self.0, f)
151             }
152         }
153 
154         Box::new(StringError(s))
155     }
156 }
157 
158 impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
from(s: Box<dyn std::error::Error + Send + Sync>) -> Self159     fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self {
160         #[derive(thiserror::Error)]
161         #[error(transparent)]
162         struct BoxedDiagnostic(Box<dyn std::error::Error + Send + Sync>);
163         impl fmt::Debug for BoxedDiagnostic {
164             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165                 fmt::Debug::fmt(&self.0, f)
166             }
167         }
168 
169         impl Diagnostic for BoxedDiagnostic {}
170 
171         Box::new(BoxedDiagnostic(s))
172     }
173 }
174 
175 /**
176 [`Diagnostic`] severity. Intended to be used by
177 [`ReportHandler`](crate::ReportHandler)s to change the way different
178 [`Diagnostic`]s are displayed. Defaults to [`Severity::Error`].
179 */
180 #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
181 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
182 #[derive(Default)]
183 pub enum Severity {
184     /// Just some help. Here's how you could be doing it better.
185     Advice,
186     /// Warning. Please take note.
187     Warning,
188     /// Critical failure. The program cannot continue.
189     /// This is the default severity, if you don't specify another one.
190     #[default]
191     Error,
192 }
193 
194 #[cfg(feature = "serde")]
195 #[test]
test_serialize_severity()196 fn test_serialize_severity() {
197     use serde_json::json;
198 
199     assert_eq!(json!(Severity::Advice), json!("Advice"));
200     assert_eq!(json!(Severity::Warning), json!("Warning"));
201     assert_eq!(json!(Severity::Error), json!("Error"));
202 }
203 
204 #[cfg(feature = "serde")]
205 #[test]
test_deserialize_severity()206 fn test_deserialize_severity() {
207     use serde_json::json;
208 
209     let severity: Severity = serde_json::from_value(json!("Advice")).unwrap();
210     assert_eq!(severity, Severity::Advice);
211 
212     let severity: Severity = serde_json::from_value(json!("Warning")).unwrap();
213     assert_eq!(severity, Severity::Warning);
214 
215     let severity: Severity = serde_json::from_value(json!("Error")).unwrap();
216     assert_eq!(severity, Severity::Error);
217 }
218 
219 /**
220 Represents readable source code of some sort.
221 
222 This trait is able to support simple `SourceCode` types like [`String`]s, as
223 well as more involved types like indexes into centralized `SourceMap`-like
224 types, file handles, and even network streams.
225 
226 If you can read it, you can source it, and it's not necessary to read the
227 whole thing--meaning you should be able to support `SourceCode`s which are
228 gigabytes or larger in size.
229 */
230 pub trait SourceCode: Send + Sync {
231     /// Read the bytes for a specific span from this `SourceCode`, keeping a
232     /// certain number of lines before and after the span as context.
read_span<'a>( &'a self, span: &SourceSpan, context_lines_before: usize, context_lines_after: usize, ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError>233     fn read_span<'a>(
234         &'a self,
235         span: &SourceSpan,
236         context_lines_before: usize,
237         context_lines_after: usize,
238     ) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError>;
239 }
240 
241 /// A labeled [`SourceSpan`].
242 #[derive(Debug, Clone, PartialEq, Eq)]
243 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
244 pub struct LabeledSpan {
245     #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
246     label: Option<String>,
247     span: SourceSpan,
248     primary: bool,
249 }
250 
251 impl LabeledSpan {
252     /// Makes a new labeled span.
new(label: Option<String>, offset: ByteOffset, len: usize) -> Self253     pub const fn new(label: Option<String>, offset: ByteOffset, len: usize) -> Self {
254         Self {
255             label,
256             span: SourceSpan::new(SourceOffset(offset), len),
257             primary: false,
258         }
259     }
260 
261     /// Makes a new labeled span using an existing span.
new_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self262     pub fn new_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self {
263         Self {
264             label,
265             span: span.into(),
266             primary: false,
267         }
268     }
269 
270     /// Makes a new labeled primary span using an existing span.
new_primary_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self271     pub fn new_primary_with_span(label: Option<String>, span: impl Into<SourceSpan>) -> Self {
272         Self {
273             label,
274             span: span.into(),
275             primary: true,
276         }
277     }
278 
279     /// Makes a new label at specified span
280     ///
281     /// # Examples
282     /// ```
283     /// use miette::LabeledSpan;
284     ///
285     /// let source = "Cpp is the best";
286     /// let label = LabeledSpan::at(0..3, "should be Rust");
287     /// assert_eq!(
288     ///     label,
289     ///     LabeledSpan::new(Some("should be Rust".to_string()), 0, 3)
290     /// )
291     /// ```
at(span: impl Into<SourceSpan>, label: impl Into<String>) -> Self292     pub fn at(span: impl Into<SourceSpan>, label: impl Into<String>) -> Self {
293         Self::new_with_span(Some(label.into()), span)
294     }
295 
296     /// Makes a new label that points at a specific offset.
297     ///
298     /// # Examples
299     /// ```
300     /// use miette::LabeledSpan;
301     ///
302     /// let source = "(2 + 2";
303     /// let label = LabeledSpan::at_offset(4, "expected a closing parenthesis");
304     /// assert_eq!(
305     ///     label,
306     ///     LabeledSpan::new(Some("expected a closing parenthesis".to_string()), 4, 0)
307     /// )
308     /// ```
at_offset(offset: ByteOffset, label: impl Into<String>) -> Self309     pub fn at_offset(offset: ByteOffset, label: impl Into<String>) -> Self {
310         Self::new(Some(label.into()), offset, 0)
311     }
312 
313     /// Makes a new label without text, that underlines a specific span.
314     ///
315     /// # Examples
316     /// ```
317     /// use miette::LabeledSpan;
318     ///
319     /// let source = "You have an eror here";
320     /// let label = LabeledSpan::underline(12..16);
321     /// assert_eq!(label, LabeledSpan::new(None, 12, 4))
322     /// ```
underline(span: impl Into<SourceSpan>) -> Self323     pub fn underline(span: impl Into<SourceSpan>) -> Self {
324         Self::new_with_span(None, span)
325     }
326 
327     /// Gets the (optional) label string for this `LabeledSpan`.
label(&self) -> Option<&str>328     pub fn label(&self) -> Option<&str> {
329         self.label.as_deref()
330     }
331 
332     /// Returns a reference to the inner [`SourceSpan`].
inner(&self) -> &SourceSpan333     pub const fn inner(&self) -> &SourceSpan {
334         &self.span
335     }
336 
337     /// Returns the 0-based starting byte offset.
offset(&self) -> usize338     pub const fn offset(&self) -> usize {
339         self.span.offset()
340     }
341 
342     /// Returns the number of bytes this `LabeledSpan` spans.
len(&self) -> usize343     pub const fn len(&self) -> usize {
344         self.span.len()
345     }
346 
347     /// True if this `LabeledSpan` is empty.
is_empty(&self) -> bool348     pub const fn is_empty(&self) -> bool {
349         self.span.is_empty()
350     }
351 
352     /// True if this `LabeledSpan` is a primary span.
primary(&self) -> bool353     pub const fn primary(&self) -> bool {
354         self.primary
355     }
356 }
357 
358 #[cfg(feature = "serde")]
359 #[test]
test_serialize_labeled_span()360 fn test_serialize_labeled_span() {
361     use serde_json::json;
362 
363     assert_eq!(
364         json!(LabeledSpan::new(None, 0, 0)),
365         json!({
366             "span": { "offset": 0, "length": 0, },
367             "primary": false,
368         })
369     );
370 
371     assert_eq!(
372         json!(LabeledSpan::new(Some("label".to_string()), 0, 0)),
373         json!({
374             "label": "label",
375             "span": { "offset": 0, "length": 0, },
376             "primary": false,
377         })
378     );
379 }
380 
381 #[cfg(feature = "serde")]
382 #[test]
test_deserialize_labeled_span()383 fn test_deserialize_labeled_span() {
384     use serde_json::json;
385 
386     let span: LabeledSpan = serde_json::from_value(json!({
387         "label": null,
388         "span": { "offset": 0, "length": 0, },
389         "primary": false,
390     }))
391     .unwrap();
392     assert_eq!(span, LabeledSpan::new(None, 0, 0));
393 
394     let span: LabeledSpan = serde_json::from_value(json!({
395         "span": { "offset": 0, "length": 0, },
396         "primary": false
397     }))
398     .unwrap();
399     assert_eq!(span, LabeledSpan::new(None, 0, 0));
400 
401     let span: LabeledSpan = serde_json::from_value(json!({
402         "label": "label",
403         "span": { "offset": 0, "length": 0, },
404         "primary": false
405     }))
406     .unwrap();
407     assert_eq!(span, LabeledSpan::new(Some("label".to_string()), 0, 0));
408 }
409 
410 /**
411 Contents of a [`SourceCode`] covered by [`SourceSpan`].
412 
413 Includes line and column information to optimize highlight calculations.
414 */
415 pub trait SpanContents<'a> {
416     /// Reference to the data inside the associated span, in bytes.
data(&self) -> &'a [u8]417     fn data(&self) -> &'a [u8];
418     /// [`SourceSpan`] representing the span covered by this `SpanContents`.
span(&self) -> &SourceSpan419     fn span(&self) -> &SourceSpan;
420     /// An optional (file?) name for the container of this `SpanContents`.
name(&self) -> Option<&str>421     fn name(&self) -> Option<&str> {
422         None
423     }
424     /// The 0-indexed line in the associated [`SourceCode`] where the data
425     /// begins.
line(&self) -> usize426     fn line(&self) -> usize;
427     /// The 0-indexed column in the associated [`SourceCode`] where the data
428     /// begins, relative to `line`.
column(&self) -> usize429     fn column(&self) -> usize;
430     /// Total number of lines covered by this `SpanContents`.
line_count(&self) -> usize431     fn line_count(&self) -> usize;
432 
433     /// Optional method. The language name for this source code, if any.
434     /// This is used to drive syntax highlighting.
435     ///
436     /// Examples: Rust, TOML, C
437     ///
language(&self) -> Option<&str>438     fn language(&self) -> Option<&str> {
439         None
440     }
441 }
442 
443 /**
444 Basic implementation of the [`SpanContents`] trait, for convenience.
445 */
446 #[derive(Clone, Debug)]
447 pub struct MietteSpanContents<'a> {
448     // Data from a [`SourceCode`], in bytes.
449     data: &'a [u8],
450     // span actually covered by this SpanContents.
451     span: SourceSpan,
452     // The 0-indexed line where the associated [`SourceSpan`] _starts_.
453     line: usize,
454     // The 0-indexed column where the associated [`SourceSpan`] _starts_.
455     column: usize,
456     // Number of line in this snippet.
457     line_count: usize,
458     // Optional filename
459     name: Option<String>,
460     // Optional language
461     language: Option<String>,
462 }
463 
464 impl<'a> MietteSpanContents<'a> {
465     /// Make a new [`MietteSpanContents`] object.
new( data: &'a [u8], span: SourceSpan, line: usize, column: usize, line_count: usize, ) -> MietteSpanContents<'a>466     pub const fn new(
467         data: &'a [u8],
468         span: SourceSpan,
469         line: usize,
470         column: usize,
471         line_count: usize,
472     ) -> MietteSpanContents<'a> {
473         MietteSpanContents {
474             data,
475             span,
476             line,
477             column,
478             line_count,
479             name: None,
480             language: None,
481         }
482     }
483 
484     /// Make a new [`MietteSpanContents`] object, with a name for its 'file'.
new_named( name: String, data: &'a [u8], span: SourceSpan, line: usize, column: usize, line_count: usize, ) -> MietteSpanContents<'a>485     pub const fn new_named(
486         name: String,
487         data: &'a [u8],
488         span: SourceSpan,
489         line: usize,
490         column: usize,
491         line_count: usize,
492     ) -> MietteSpanContents<'a> {
493         MietteSpanContents {
494             data,
495             span,
496             line,
497             column,
498             line_count,
499             name: Some(name),
500             language: None,
501         }
502     }
503 
504     /// Sets the [`language`](SourceCode::language) for syntax highlighting.
with_language(mut self, language: impl Into<String>) -> Self505     pub fn with_language(mut self, language: impl Into<String>) -> Self {
506         self.language = Some(language.into());
507         self
508     }
509 }
510 
511 impl<'a> SpanContents<'a> for MietteSpanContents<'a> {
data(&self) -> &'a [u8]512     fn data(&self) -> &'a [u8] {
513         self.data
514     }
span(&self) -> &SourceSpan515     fn span(&self) -> &SourceSpan {
516         &self.span
517     }
line(&self) -> usize518     fn line(&self) -> usize {
519         self.line
520     }
column(&self) -> usize521     fn column(&self) -> usize {
522         self.column
523     }
line_count(&self) -> usize524     fn line_count(&self) -> usize {
525         self.line_count
526     }
name(&self) -> Option<&str>527     fn name(&self) -> Option<&str> {
528         self.name.as_deref()
529     }
language(&self) -> Option<&str>530     fn language(&self) -> Option<&str> {
531         self.language.as_deref()
532     }
533 }
534 
535 /// Span within a [`SourceCode`]
536 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
537 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
538 pub struct SourceSpan {
539     /// The start of the span.
540     offset: SourceOffset,
541     /// The total length of the span
542     length: usize,
543 }
544 
545 impl SourceSpan {
546     /// Create a new [`SourceSpan`].
new(start: SourceOffset, length: usize) -> Self547     pub const fn new(start: SourceOffset, length: usize) -> Self {
548         Self {
549             offset: start,
550             length,
551         }
552     }
553 
554     /// The absolute offset, in bytes, from the beginning of a [`SourceCode`].
offset(&self) -> usize555     pub const fn offset(&self) -> usize {
556         self.offset.offset()
557     }
558 
559     /// Total length of the [`SourceSpan`], in bytes.
len(&self) -> usize560     pub const fn len(&self) -> usize {
561         self.length
562     }
563 
564     /// Whether this [`SourceSpan`] has a length of zero. It may still be useful
565     /// to point to a specific point.
is_empty(&self) -> bool566     pub const fn is_empty(&self) -> bool {
567         self.length == 0
568     }
569 }
570 
571 impl From<(ByteOffset, usize)> for SourceSpan {
from((start, len): (ByteOffset, usize)) -> Self572     fn from((start, len): (ByteOffset, usize)) -> Self {
573         Self {
574             offset: start.into(),
575             length: len,
576         }
577     }
578 }
579 
580 impl From<(SourceOffset, usize)> for SourceSpan {
from((start, len): (SourceOffset, usize)) -> Self581     fn from((start, len): (SourceOffset, usize)) -> Self {
582         Self::new(start, len)
583     }
584 }
585 
586 impl From<std::ops::Range<ByteOffset>> for SourceSpan {
from(range: std::ops::Range<ByteOffset>) -> Self587     fn from(range: std::ops::Range<ByteOffset>) -> Self {
588         Self {
589             offset: range.start.into(),
590             length: range.len(),
591         }
592     }
593 }
594 
595 impl From<SourceOffset> for SourceSpan {
from(offset: SourceOffset) -> Self596     fn from(offset: SourceOffset) -> Self {
597         Self { offset, length: 0 }
598     }
599 }
600 
601 impl From<ByteOffset> for SourceSpan {
from(offset: ByteOffset) -> Self602     fn from(offset: ByteOffset) -> Self {
603         Self {
604             offset: offset.into(),
605             length: 0,
606         }
607     }
608 }
609 
610 #[cfg(feature = "serde")]
611 #[test]
test_serialize_source_span()612 fn test_serialize_source_span() {
613     use serde_json::json;
614 
615     assert_eq!(
616         json!(SourceSpan::from(0)),
617         json!({ "offset": 0, "length": 0})
618     );
619 }
620 
621 #[cfg(feature = "serde")]
622 #[test]
test_deserialize_source_span()623 fn test_deserialize_source_span() {
624     use serde_json::json;
625 
626     let span: SourceSpan = serde_json::from_value(json!({ "offset": 0, "length": 0})).unwrap();
627     assert_eq!(span, SourceSpan::from(0));
628 }
629 
630 /**
631 "Raw" type for the byte offset from the beginning of a [`SourceCode`].
632 */
633 pub type ByteOffset = usize;
634 
635 /**
636 Newtype that represents the [`ByteOffset`] from the beginning of a [`SourceCode`]
637 */
638 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
639 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
640 pub struct SourceOffset(ByteOffset);
641 
642 impl SourceOffset {
643     /// Actual byte offset.
offset(&self) -> ByteOffset644     pub const fn offset(&self) -> ByteOffset {
645         self.0
646     }
647 
648     /// Little utility to help convert 1-based line/column locations into
649     /// miette-compatible Spans
650     ///
651     /// This function is infallible: Giving an out-of-range line/column pair
652     /// will return the offset of the last byte in the source.
from_location(source: impl AsRef<str>, loc_line: usize, loc_col: usize) -> Self653     pub fn from_location(source: impl AsRef<str>, loc_line: usize, loc_col: usize) -> Self {
654         let mut line = 0usize;
655         let mut col = 0usize;
656         let mut offset = 0usize;
657         for char in source.as_ref().chars() {
658             if line + 1 >= loc_line && col + 1 >= loc_col {
659                 break;
660             }
661             if char == '\n' {
662                 col = 0;
663                 line += 1;
664             } else {
665                 col += 1;
666             }
667             offset += char.len_utf8();
668         }
669 
670         SourceOffset(offset)
671     }
672 
673     /// Returns an offset for the _file_ location of wherever this function is
674     /// called. If you want to get _that_ caller's location, mark this
675     /// function's caller with `#[track_caller]` (and so on and so forth).
676     ///
677     /// Returns both the filename that was given and the offset of the caller
678     /// as a [`SourceOffset`].
679     ///
680     /// Keep in mind that this fill only work if the file your Rust source
681     /// file was compiled from is actually available at that location. If
682     /// you're shipping binaries for your application, you'll want to ignore
683     /// the Err case or otherwise report it.
684     #[track_caller]
from_current_location() -> Result<(String, Self), MietteError>685     pub fn from_current_location() -> Result<(String, Self), MietteError> {
686         let loc = Location::caller();
687         Ok((
688             loc.file().into(),
689             fs::read_to_string(loc.file())
690                 .map(|txt| Self::from_location(txt, loc.line() as usize, loc.column() as usize))?,
691         ))
692     }
693 }
694 
695 impl From<ByteOffset> for SourceOffset {
from(bytes: ByteOffset) -> Self696     fn from(bytes: ByteOffset) -> Self {
697         SourceOffset(bytes)
698     }
699 }
700 
701 #[test]
test_source_offset_from_location()702 fn test_source_offset_from_location() {
703     let source = "f\n\noo\r\nbar";
704 
705     assert_eq!(SourceOffset::from_location(source, 1, 1).offset(), 0);
706     assert_eq!(SourceOffset::from_location(source, 1, 2).offset(), 1);
707     assert_eq!(SourceOffset::from_location(source, 2, 1).offset(), 2);
708     assert_eq!(SourceOffset::from_location(source, 3, 1).offset(), 3);
709     assert_eq!(SourceOffset::from_location(source, 3, 2).offset(), 4);
710     assert_eq!(SourceOffset::from_location(source, 3, 3).offset(), 5);
711     assert_eq!(SourceOffset::from_location(source, 3, 4).offset(), 6);
712     assert_eq!(SourceOffset::from_location(source, 4, 1).offset(), 7);
713     assert_eq!(SourceOffset::from_location(source, 4, 2).offset(), 8);
714     assert_eq!(SourceOffset::from_location(source, 4, 3).offset(), 9);
715     assert_eq!(SourceOffset::from_location(source, 4, 4).offset(), 10);
716 
717     // Out-of-range
718     assert_eq!(
719         SourceOffset::from_location(source, 5, 1).offset(),
720         source.len()
721     );
722 }
723 
724 #[cfg(feature = "serde")]
725 #[test]
test_serialize_source_offset()726 fn test_serialize_source_offset() {
727     use serde_json::json;
728 
729     assert_eq!(json!(SourceOffset::from(0)), 0);
730 }
731 
732 #[cfg(feature = "serde")]
733 #[test]
test_deserialize_source_offset()734 fn test_deserialize_source_offset() {
735     let offset: SourceOffset = serde_json::from_str("0").unwrap();
736     assert_eq!(offset, SourceOffset::from(0));
737 }
738