Skip to main content

virtue_next/
error.rs

1use crate::{
2    generate::{PushParseError, StreamBuilder},
3    prelude::*,
4};
5use std::fmt;
6
7/// Errors that can occur while parsing or generator your derive macro.
8#[derive(Debug)]
9pub enum Error {
10    /// The data type at `Span` is unknown. This will be called when [`Parse::new`] is called on anything that is not a `struct` or `enum`.
11    ///
12    /// [`Parse::new`]: enum.Parse.html#method.new
13    UnknownDataType(Span),
14
15    /// The rust syntax is invalid. This can be returned while parsing the Enum or Struct.
16    ///
17    /// This error is assumed to not appear as rustc will do syntax checking before virtue gets access to the [`TokenStream`].
18    /// However this error could still be returned.
19    InvalidRustSyntax {
20        /// The span at which the invalid syntax is found
21        span: Span,
22        /// The expected rust syntax when this parsing occured
23        expected: String,
24    },
25
26    /// Expected an ident at the given span.
27    ExpectedIdent(Span),
28
29    /// Failed to parse the code passed to [`StreamBuilder::push_parsed`].
30    ///
31    /// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed
32    PushParse {
33        /// An optional span. Normally this is `None`, unless `.with_span` is called.
34        span: Option<Span>,
35        /// The internal parse error
36        error: PushParseError,
37    },
38
39    /// A custom error thrown by the developer
40    Custom {
41        /// The error message
42        error: String,
43        /// Optionally the position that the error occurred at
44        span: Option<Span>,
45    },
46}
47
48impl From<PushParseError> for Error {
49    fn from(e: PushParseError) -> Self {
50        Self::PushParse {
51            span: None,
52            error: e,
53        }
54    }
55}
56
57impl Error {
58    /// Throw a custom error
59    pub fn custom(s: impl Into<String>) -> Self {
60        Self::Custom {
61            error: s.into(),
62            span: None,
63        }
64    }
65
66    /// Throw a custom error at a given location
67    pub fn custom_at(s: impl Into<String>, span: Span) -> Self {
68        Self::Custom {
69            error: s.into(),
70            span: Some(span),
71        }
72    }
73
74    /// Throw a custom error at a given token
75    pub fn custom_at_token(s: impl Into<String>, token: TokenTree) -> Self {
76        Self::Custom {
77            error: s.into(),
78            span: Some(token.span()),
79        }
80    }
81
82    /// Throw a custom error at a given `Option<TokenTree>`
83    pub fn custom_at_opt_token(s: impl Into<String>, token: Option<TokenTree>) -> Self {
84        Self::Custom {
85            error: s.into(),
86            span: token.map(|t| t.span()),
87        }
88    }
89
90    pub(crate) fn wrong_token<T>(token: Option<&TokenTree>, expected: &str) -> Result<T> {
91        Err(Self::InvalidRustSyntax {
92            span: token.map(|t| t.span()).unwrap_or_else(Span::call_site),
93            expected: format!("{}, got {:?}", expected, token),
94        })
95    }
96
97    /// Return a new error that is located at the given span
98    pub fn with_span(mut self, new_span: Span) -> Self {
99        match &mut self {
100            Error::UnknownDataType(span) => *span = new_span,
101            Error::InvalidRustSyntax { span, .. } => *span = new_span,
102            Error::ExpectedIdent(span) => *span = new_span,
103            Error::PushParse { span, .. } => {
104                *span = Some(new_span);
105            }
106            Error::Custom { span, .. } => *span = Some(new_span),
107        }
108
109        self
110    }
111}
112
113// helper functions for the unit tests
114#[cfg(test)]
115impl Error {
116    /// helper functions for the unit tests
117    pub fn is_unknown_data_type(&self) -> bool {
118        matches!(self, Error::UnknownDataType(_))
119    }
120
121    /// helper functions for the unit tests
122    pub fn is_invalid_rust_syntax(&self) -> bool {
123        matches!(self, Error::InvalidRustSyntax { .. })
124    }
125}
126
127impl fmt::Display for Error {
128    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
129        match self {
130            Self::UnknownDataType(_) => {
131                write!(fmt, "Unknown data type, only enum and struct are supported")
132            }
133            Self::InvalidRustSyntax { expected, .. } => {
134                write!(fmt, "Invalid rust syntax, expected {}", expected)
135            }
136            Self::ExpectedIdent(_) => write!(fmt, "Expected ident"),
137            Self::PushParse { error, .. } => write!(
138                fmt,
139                "Invalid code passed to `StreamBuilder::push_parsed`: {:?}",
140                error
141            ),
142            Self::Custom { error, .. } => write!(fmt, "{}", error),
143        }
144    }
145}
146
147impl Error {
148    /// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user.
149    pub fn into_token_stream(self) -> TokenStream {
150        let maybe_span = match &self {
151            Self::UnknownDataType(span)
152            | Self::ExpectedIdent(span)
153            | Self::InvalidRustSyntax { span, .. } => Some(*span),
154            Self::Custom { span, .. } | Self::PushParse { span, .. } => *span,
155        };
156        self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site))
157    }
158
159    /// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user. The error will be shown at the given `span`.
160    pub fn throw_with_span(self, span: Span) -> TokenStream {
161        // compile_error!($message)
162        let mut builder = StreamBuilder::new();
163        builder.ident_str("compile_error");
164        builder.punct('!');
165        builder
166            .group(Delimiter::Brace, |b| {
167                b.lit_str(self.to_string());
168                Ok(())
169            })
170            .unwrap();
171        builder.set_span_on_all_tokens(span);
172        builder.stream
173    }
174}