wax/diagnostics/
mod.rs

1mod miette;
2
3#[cfg(feature = "miette")]
4use ::miette::LabeledSpan;
5use std::cmp;
6use std::fmt::{self, Display, Formatter};
7
8#[cfg(feature = "miette")]
9pub use crate::diagnostics::miette::diagnose;
10
11/// Location and length of a token within a glob expression.
12///
13/// Spans are encoded as a tuple of `usize`s, where the first element is the location or position
14/// and the second element is the length. Both position and length are measured in bytes and
15/// **not** code points, graphemes, etc.
16///
17/// # Examples
18///
19/// Spans can be used to isolate sub-expressions.
20///
21/// ```rust
22/// use wax::Glob;
23///
24/// let expression = "**/*.txt";
25/// let glob = Glob::new(expression).unwrap();
26/// for token in glob.captures() {
27///     let (start, n) = token.span();
28///     println!("capturing sub-expression: {}", &expression[start..][..n]);
29/// }
30/// ```
31pub type Span = (usize, usize);
32
33pub trait SpanExt {
34    fn union(&self, other: &Self) -> Self;
35}
36
37impl SpanExt for Span {
38    fn union(&self, other: &Self) -> Self {
39        let start = cmp::min(self.0, other.0);
40        let end = cmp::max(self.0 + self.1, other.0 + other.1);
41        (start, end - start)
42    }
43}
44
45/// Error associated with a [`Span`] within a glob expression.
46///
47/// Located errors describe specific instances of an error within a glob expression. Types that
48/// implement this trait provide a location within a glob expression via the [`LocatedError::span`]
49/// function as well as a description via the [`Display`] trait. See [`BuildError::locations`].
50///
51/// [`BuildError::locations`]: crate::BuildError::locations
52/// [`Display`]: std::fmt::Display
53/// [`LocatedError::span`]: crate::LocatedError::span
54/// [`Span`]: crate::Span
55pub trait LocatedError: Display {
56    /// Gets the span within the glob expression with which the error is associated.
57    fn span(&self) -> Span;
58}
59
60#[derive(Clone, Copy, Debug)]
61pub struct CompositeSpan {
62    label: &'static str,
63    kind: CompositeSpanKind,
64}
65
66impl CompositeSpan {
67    pub fn spanned(label: &'static str, span: Span) -> Self {
68        CompositeSpan {
69            label,
70            kind: CompositeSpanKind::Span(span),
71        }
72    }
73
74    pub fn correlated(label: &'static str, span: Span, correlated: CorrelatedSpan) -> Self {
75        CompositeSpan {
76            label,
77            kind: CompositeSpanKind::Correlated { span, correlated },
78        }
79    }
80
81    #[cfg(feature = "miette")]
82    pub fn labels(&self) -> Vec<LabeledSpan> {
83        let label = Some(self.label.to_string());
84        match self.kind {
85            CompositeSpanKind::Span(ref span) => vec![LabeledSpan::new_with_span(label, *span)],
86            CompositeSpanKind::Correlated {
87                ref span,
88                ref correlated,
89            } => Some(LabeledSpan::new_with_span(label, *span))
90                .into_iter()
91                .chain(correlated.labels())
92                .collect(),
93        }
94    }
95}
96
97impl Display for CompositeSpan {
98    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
99        write!(f, "{}", self.label)
100    }
101}
102
103impl LocatedError for CompositeSpan {
104    fn span(&self) -> Span {
105        match self.kind {
106            CompositeSpanKind::Span(ref span) | CompositeSpanKind::Correlated { ref span, .. } => {
107                *span
108            },
109        }
110    }
111}
112
113#[derive(Clone, Copy, Debug)]
114enum CompositeSpanKind {
115    Span(Span),
116    Correlated {
117        span: Span,
118        #[cfg_attr(not(feature = "miette"), allow(dead_code))]
119        correlated: CorrelatedSpan,
120    },
121}
122
123#[derive(Clone, Copy, Debug)]
124pub enum CorrelatedSpan {
125    Contiguous(Span),
126    Split(Span, Span),
127}
128
129impl CorrelatedSpan {
130    pub fn split_some(left: Option<Span>, right: Span) -> Self {
131        if let Some(left) = left {
132            CorrelatedSpan::Split(left, right)
133        }
134        else {
135            CorrelatedSpan::Contiguous(right)
136        }
137    }
138
139    #[cfg(feature = "miette")]
140    pub fn labels(&self) -> Vec<LabeledSpan> {
141        let label = Some("here".to_string());
142        match self {
143            CorrelatedSpan::Contiguous(ref span) => {
144                vec![LabeledSpan::new_with_span(label, *span)]
145            },
146            CorrelatedSpan::Split(ref left, ref right) => vec![
147                LabeledSpan::new_with_span(label.clone(), *left),
148                LabeledSpan::new_with_span(label, *right),
149            ],
150        }
151    }
152}
153
154impl From<Span> for CorrelatedSpan {
155    fn from(span: Span) -> Self {
156        CorrelatedSpan::Contiguous(span)
157    }
158}