target_spec_miette/
imp.rs

1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use miette::{Diagnostic, LabeledSpan, SourceCode, SourceOffset, SourceSpan};
5use std::{error::Error as StdError, fmt};
6use target_spec::errors::{
7    CustomTripleCreateError, Error as TargetSpecError, ExpressionParseError, PlainStringParseError,
8    TripleParseError,
9};
10
11/// Extension trait that converts errors into a [`miette::Diagnostic`].
12pub trait IntoMietteDiagnostic {
13    /// The `Diagnostic` type that `self` will be converted to.
14    type IntoDiagnostic;
15
16    /// Converts the underlying error into [`Self::IntoDiagnostic`].
17    ///
18    /// This can be used to pretty-print errors returned by target-spec.
19    fn into_diagnostic(self) -> Self::IntoDiagnostic;
20}
21
22impl IntoMietteDiagnostic for TargetSpecError {
23    type IntoDiagnostic = Box<dyn Diagnostic + Send + Sync + 'static>;
24
25    fn into_diagnostic(self) -> Self::IntoDiagnostic {
26        match self {
27            Self::InvalidExpression(error) => Box::new(error.into_diagnostic()),
28            Self::InvalidTargetSpecString(error) => Box::new(error.into_diagnostic()),
29            Self::UnknownPlatformTriple(error) => Box::new(error.into_diagnostic()),
30            #[allow(deprecated)]
31            Self::CustomTripleCreate(error) => Box::new(error.into_diagnostic()),
32            Self::CustomPlatformCreate(error) => Box::new(error.into_diagnostic()),
33            other => Box::<dyn Diagnostic + Send + Sync + 'static>::from(other.to_string()),
34        }
35    }
36}
37
38/// A wrapper around [`ExpressionParseError`] that implements [`Diagnostic`].
39#[derive(Clone, PartialEq, Eq)]
40pub struct ExpressionParseDiagnostic(ExpressionParseError);
41
42impl ExpressionParseDiagnostic {
43    /// Creates a new `ExpressionParseDiagnostic`.
44    pub fn new(error: ExpressionParseError) -> Self {
45        Self(error)
46    }
47}
48
49impl fmt::Debug for ExpressionParseDiagnostic {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        fmt::Debug::fmt(&self.0, f)
52    }
53}
54
55impl fmt::Display for ExpressionParseDiagnostic {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        fmt::Display::fmt(&self.0, f)
58    }
59}
60
61impl StdError for ExpressionParseDiagnostic {
62    fn source(&self) -> Option<&(dyn StdError + 'static)> {
63        self.0.source()
64    }
65}
66
67impl Diagnostic for ExpressionParseDiagnostic {
68    fn source_code(&self) -> Option<&dyn SourceCode> {
69        Some(&self.0.input)
70    }
71
72    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
73        let label = LabeledSpan::new_with_span(Some(self.0.kind.to_string()), self.0.span.clone());
74        Some(Box::new(std::iter::once(label)))
75    }
76}
77
78impl IntoMietteDiagnostic for ExpressionParseError {
79    type IntoDiagnostic = ExpressionParseDiagnostic;
80
81    fn into_diagnostic(self) -> Self::IntoDiagnostic {
82        ExpressionParseDiagnostic::new(self)
83    }
84}
85
86/// A wrapper around [`TripleParseError`] that implements [`Diagnostic`].
87#[derive(Clone, PartialEq, Eq)]
88pub struct TripleParseDiagnostic {
89    error: TripleParseError,
90    // Need to store this separately because &str can't be cast to &dyn SourceCode.
91    triple_str: String,
92}
93
94impl TripleParseDiagnostic {
95    /// Creates a new `ExpressionParseDiagnostic`.
96    pub fn new(error: TripleParseError) -> Self {
97        let triple_str = error.triple_str().to_owned();
98        Self { error, triple_str }
99    }
100}
101
102impl fmt::Debug for TripleParseDiagnostic {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        fmt::Debug::fmt(&self.error, f)
105    }
106}
107
108impl fmt::Display for TripleParseDiagnostic {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        fmt::Display::fmt(&self.error, f)
111    }
112}
113
114impl StdError for TripleParseDiagnostic {
115    fn source(&self) -> Option<&(dyn StdError + 'static)> {
116        self.error.source()
117    }
118}
119
120impl Diagnostic for TripleParseDiagnostic {
121    fn source_code(&self) -> Option<&dyn SourceCode> {
122        Some(&self.triple_str as &dyn SourceCode)
123    }
124
125    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
126        let label = LabeledSpan::new_with_span(
127            Some(
128                self.error
129                    .source()
130                    .expect("TripleParseError always returns a source")
131                    .to_string(),
132            ),
133            (0, self.triple_str.len()),
134        );
135        Some(Box::new(std::iter::once(label)))
136    }
137}
138
139impl IntoMietteDiagnostic for TripleParseError {
140    type IntoDiagnostic = TripleParseDiagnostic;
141
142    fn into_diagnostic(self) -> Self::IntoDiagnostic {
143        TripleParseDiagnostic::new(self)
144    }
145}
146
147/// A wrapper around [`PlainStringParseError`] that implements [`Diagnostic`].
148#[derive(Clone, PartialEq, Eq)]
149pub struct PlainStringParseDiagnostic {
150    error: PlainStringParseError,
151    // Need to store this separately because &str can't be cast to &dyn SourceCode.
152    input: String,
153}
154
155impl PlainStringParseDiagnostic {
156    /// Creates a new `ExpressionParseDiagnostic`.
157    pub fn new(error: PlainStringParseError) -> Self {
158        let input = error.input.clone();
159        Self { error, input }
160    }
161}
162
163impl fmt::Debug for PlainStringParseDiagnostic {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        fmt::Debug::fmt(&self.error, f)
166    }
167}
168
169impl fmt::Display for PlainStringParseDiagnostic {
170    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171        // The full error message duplicates information produced by the diagnostic, so keep it
172        // short.
173        f.write_str("invalid triple identifier")
174    }
175}
176
177impl StdError for PlainStringParseDiagnostic {
178    fn source(&self) -> Option<&(dyn StdError + 'static)> {
179        self.error.source()
180    }
181}
182
183impl Diagnostic for PlainStringParseDiagnostic {
184    fn source_code(&self) -> Option<&dyn SourceCode> {
185        Some(&self.input as &dyn SourceCode)
186    }
187
188    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
189        let label = LabeledSpan::new_with_span(
190            Some("character must be alphanumeric, -, _ or .".to_owned()),
191            self.error.span(),
192        );
193        Some(Box::new(std::iter::once(label)))
194    }
195}
196
197impl IntoMietteDiagnostic for PlainStringParseError {
198    type IntoDiagnostic = PlainStringParseDiagnostic;
199
200    fn into_diagnostic(self) -> Self::IntoDiagnostic {
201        PlainStringParseDiagnostic::new(self)
202    }
203}
204
205impl IntoMietteDiagnostic for CustomTripleCreateError {
206    type IntoDiagnostic = CustomTripleCreateDiagnostic;
207
208    fn into_diagnostic(self) -> Self::IntoDiagnostic {
209        CustomTripleCreateDiagnostic::new(self)
210    }
211}
212
213/// A wrapper around [`CustomTripleCreateError`] that implements [`Diagnostic`].
214pub struct CustomTripleCreateDiagnostic(CustomTripleCreateError);
215
216impl CustomTripleCreateDiagnostic {
217    /// Creates a new `CustomTripleCreateDiagnostic`.
218    pub fn new(error: CustomTripleCreateError) -> Self {
219        Self(error)
220    }
221}
222
223impl fmt::Debug for CustomTripleCreateDiagnostic {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        fmt::Debug::fmt(&self.0, f)
226    }
227}
228
229impl fmt::Display for CustomTripleCreateDiagnostic {
230    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231        fmt::Display::fmt(&self.0, f)
232    }
233}
234
235impl StdError for CustomTripleCreateDiagnostic {
236    fn source(&self) -> Option<&(dyn StdError + 'static)> {
237        // Don't show the source in case we return labels below.
238        if self.0.input().is_some() && self.0.line_and_column().is_some() {
239            None
240        } else {
241            self.0.source()
242        }
243    }
244}
245
246impl Diagnostic for CustomTripleCreateDiagnostic {
247    fn source_code(&self) -> Option<&dyn SourceCode> {
248        self.0.input_string().map(|input| input as &dyn SourceCode)
249    }
250
251    fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
252        // ughhh, clippy warns about `?` here but I don't like it:
253        // https://github.com/rust-lang/rust-clippy/issues/13804
254        let input = self.0.input()?;
255        let (line, column) = self.0.line_and_column()?;
256
257        let source_offset = SourceOffset::from_location(input, line, column);
258        // serde_json doesn't return the span of the error, just a single
259        // offset.
260        let span = SourceSpan::new(source_offset, 0);
261
262        let label = LabeledSpan::new_with_span(self.0.label(), span);
263        Some(Box::new(std::iter::once(label)))
264    }
265}