1use std::{borrow::Cow, error, fmt, string::FromUtf8Error};
7
8#[derive(Clone, Debug)]
10#[non_exhaustive]
11pub enum Error {
12 InvalidExpression(ExpressionParseError),
14 InvalidTargetSpecString(PlainStringParseError),
16 UnknownPlatformTriple(TripleParseError),
18 #[deprecated(
20 since = "3.3.0",
21 note = "this variant is no longer returned: instead, use CustomPlatformCreate"
22 )]
23 #[doc(hidden)]
24 CustomTripleCreate(CustomTripleCreateError),
25 CustomPlatformCreate(CustomTripleCreateError),
27 RustcVersionVerboseParse(RustcVersionVerboseParseError),
29}
30
31impl fmt::Display for Error {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 match self {
34 Error::InvalidExpression(_) => write!(f, "invalid cfg() expression"),
35 Error::InvalidTargetSpecString(_) => {
36 write!(f, "failed to parse target spec as a plain string")
37 }
38 Error::UnknownPlatformTriple(_) => {
39 write!(f, "unknown platform triple")
40 }
41 #[allow(deprecated)]
42 Error::CustomTripleCreate(_) => write!(f, "error creating custom triple"),
43 Error::CustomPlatformCreate(_) => {
44 write!(f, "error creating custom platform")
45 }
46 Error::RustcVersionVerboseParse(_) => {
47 write!(f, "error parsing `rustc -vV` output")
48 }
49 }
50 }
51}
52
53impl error::Error for Error {
54 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
55 match self {
56 Error::InvalidExpression(err) => Some(err),
57 Error::InvalidTargetSpecString(err) => Some(err),
58 Error::UnknownPlatformTriple(err) => Some(err),
59 #[allow(deprecated)]
60 Error::CustomTripleCreate(err) => Some(err),
61 Error::CustomPlatformCreate(err) => Some(err),
62 Error::RustcVersionVerboseParse(err) => Some(err),
63 }
64 }
65}
66
67#[derive(Clone, Debug, PartialEq, Eq)]
72#[non_exhaustive]
73pub struct ExpressionParseError {
74 pub input: String,
76
77 pub span: std::ops::Range<usize>,
80
81 pub kind: ExpressionParseErrorKind,
83}
84
85impl ExpressionParseError {
86 pub(crate) fn new(input: &str, error: cfg_expr::ParseError) -> Self {
87 let span = if input.starts_with("cfg(") && input.ends_with(')') {
90 (error.span.start + 4)..(error.span.end + 4)
91 } else {
92 error.span
93 };
94 Self {
95 input: input.to_owned(),
96 span,
97 kind: ExpressionParseErrorKind::from_cfg_expr(error.reason),
98 }
99 }
100}
101
102impl fmt::Display for ExpressionParseError {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "error parsing cfg() expression")
105 }
106}
107
108impl error::Error for ExpressionParseError {}
109
110#[derive(Clone, Debug, PartialEq, Eq)]
112#[non_exhaustive]
113pub enum ExpressionParseErrorKind {
114 InvalidNot(usize),
116 InvalidCharacters,
118 UnclosedParens,
120 UnopenedParens,
122 UnclosedQuotes,
124 UnopenedQuotes,
126 Empty,
128 Unexpected {
131 expected: &'static [&'static str],
133 },
134 InvalidInteger,
136 MultipleRootPredicates,
138 InvalidHasAtomic,
140 UnknownBuiltin,
142}
143
144impl ExpressionParseErrorKind {
145 fn from_cfg_expr(reason: cfg_expr::error::Reason) -> Self {
146 use cfg_expr::error::Reason::*;
147
148 match reason {
149 InvalidCharacters => Self::InvalidCharacters,
150 UnclosedParens => Self::UnclosedParens,
151 UnopenedParens => Self::UnopenedParens,
152 UnclosedQuotes => Self::UnclosedQuotes,
153 UnopenedQuotes => Self::UnopenedQuotes,
154 Empty => Self::Empty,
155 Unexpected(expected) => Self::Unexpected { expected },
156 InvalidNot(np) => Self::InvalidNot(np),
157 InvalidInteger => Self::InvalidInteger,
158 MultipleRootPredicates => Self::MultipleRootPredicates,
159 InvalidHasAtomic => Self::InvalidHasAtomic,
160 UnknownBuiltin => Self::UnknownBuiltin,
161 }
162 }
163}
164
165impl fmt::Display for ExpressionParseErrorKind {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 use ExpressionParseErrorKind::*;
168
169 match self {
170 InvalidCharacters => f.write_str("invalid character(s)"),
171 UnclosedParens => f.write_str("unclosed parens"),
172 UnopenedParens => f.write_str("unopened parens"),
173 UnclosedQuotes => f.write_str("unclosed quotes"),
174 UnopenedQuotes => f.write_str("unopened quotes"),
175 Empty => f.write_str("empty expression"),
176 Unexpected { expected } => {
177 if expected.len() > 1 {
178 f.write_str("expected one of ")?;
179
180 for (i, exp) in expected.iter().enumerate() {
181 f.write_fmt(format_args!("{}`{exp}`", if i > 0 { ", " } else { "" }))?;
182 }
183 f.write_str(" here")
184 } else if !expected.is_empty() {
185 f.write_fmt(format_args!("expected a `{}` here", expected[0]))
186 } else {
187 f.write_str("the term was not expected here")
188 }
189 }
190 InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {np}")),
191 InvalidInteger => f.write_str("invalid integer"),
192 MultipleRootPredicates => f.write_str("multiple root predicates"),
193 InvalidHasAtomic => f.write_str("expected integer or \"ptr\""),
194 UnknownBuiltin => f.write_str("unknown built-in"),
195 }
196 }
197}
198
199#[derive(Clone, Debug, PartialEq, Eq)]
201#[non_exhaustive]
202pub struct PlainStringParseError {
203 pub input: String,
205
206 pub char_index: usize,
208
209 pub character: char,
211}
212
213impl PlainStringParseError {
214 pub(crate) fn new(input: String, char_index: usize, character: char) -> Self {
215 Self {
216 input,
217 char_index,
218 character,
219 }
220 }
221
222 pub fn span(&self) -> std::ops::Range<usize> {
224 let end = self.char_index + self.character.len_utf8();
225 self.char_index..end
226 }
227}
228
229impl fmt::Display for PlainStringParseError {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 write!(
232 f,
233 "failed to parse `{}` at index {}: character \
234 must be alphanumeric, `-`, `_` or `.`",
235 self.input, self.char_index,
236 )
237 }
238}
239
240impl error::Error for PlainStringParseError {
241 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
242 None
243 }
244}
245
246#[derive(Clone, Debug, PartialEq, Eq)]
253pub struct TripleParseError {
254 triple_str: Cow<'static, str>,
255 kind: TripleParseErrorKind,
256}
257
258impl TripleParseError {
259 pub(crate) fn new(
260 triple_str: Cow<'static, str>,
261 lexicon_err: cfg_expr::target_lexicon::ParseError,
262 ) -> Self {
263 Self {
264 triple_str,
265 kind: TripleParseErrorKind::Lexicon(lexicon_err),
266 }
267 }
268
269 pub(crate) fn new_strict(triple_str: Cow<'static, str>) -> Self {
270 Self {
271 triple_str,
272 kind: TripleParseErrorKind::LexiconDisabled,
273 }
274 }
275
276 pub fn triple_str(&self) -> &str {
278 &self.triple_str
279 }
280}
281
282impl fmt::Display for TripleParseError {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 write!(f, "unknown triple string: {}", self.triple_str)
285 }
286}
287
288impl error::Error for TripleParseError {
289 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
290 Some(&self.kind)
291 }
292}
293
294#[derive(Clone, Debug, PartialEq, Eq)]
295enum TripleParseErrorKind {
296 Lexicon(cfg_expr::target_lexicon::ParseError),
297 LexiconDisabled,
298}
299
300impl fmt::Display for TripleParseErrorKind {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 match self {
303 Self::Lexicon(_) => write!(
304 f,
305 "triple not in builtin platforms and heuristic parsing failed"
306 ),
307 Self::LexiconDisabled => write!(
308 f,
309 "triple not in builtin platforms and heuristic parsing disabled"
310 ),
311 }
312 }
313}
314
315impl error::Error for TripleParseErrorKind {
316 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
317 match self {
318 Self::Lexicon(error) => Some(error),
319 Self::LexiconDisabled => None,
320 }
321 }
322}
323
324#[derive(Clone, Debug)]
326#[non_exhaustive]
327pub enum CustomTripleCreateError {
328 #[cfg(feature = "custom")]
329 #[deprecated(
331 since = "3.3.0",
332 note = "this variant is no longer returned: instead, \
333 use DeserializeJson which also includes the input string"
334 )]
335 #[doc(hidden)]
336 Deserialize {
337 triple: String,
339
340 error: std::sync::Arc<serde_json::Error>,
342 },
343
344 Unavailable,
349
350 #[cfg(feature = "custom")]
351 DeserializeJson {
353 triple: String,
355
356 input: String,
358
359 error: std::sync::Arc<serde_json::Error>,
361 },
362}
363
364impl CustomTripleCreateError {
365 #[inline]
367 pub fn input(&self) -> Option<&str> {
368 self.input_string().map(String::as_str)
369 }
370
371 pub fn input_string(&self) -> Option<&String> {
377 match self {
378 #[cfg(feature = "custom")]
379 Self::DeserializeJson { input, .. } => Some(input),
380 #[cfg(feature = "custom")]
381 #[allow(deprecated)]
382 Self::Deserialize { .. } => None,
383 Self::Unavailable => None,
384 }
385 }
386
387 #[inline]
393 pub fn line_and_column(&self) -> Option<(usize, usize)> {
394 match self {
395 #[cfg(feature = "custom")]
396 Self::DeserializeJson { error, .. } => Some((error.line(), error.column())),
397 #[cfg(feature = "custom")]
398 #[allow(deprecated)]
399 Self::Deserialize { .. } => None,
400 Self::Unavailable => None,
401 }
402 }
403
404 pub fn label(&self) -> Option<String> {
409 match self {
410 #[cfg(feature = "custom")]
411 Self::DeserializeJson { error, .. } => {
412 let label = error.to_string();
413 let trimmed = match label.rfind(" at line ") {
415 Some(idx) => label[..idx].to_string(),
416 None => label,
417 };
418 Some(trimmed)
419 }
420 #[cfg(feature = "custom")]
421 #[allow(deprecated)]
422 Self::Deserialize { .. } => None,
423 Self::Unavailable => None,
424 }
425 }
426}
427
428impl fmt::Display for CustomTripleCreateError {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 match self {
431 #[cfg(feature = "custom")]
432 #[allow(deprecated)]
433 Self::DeserializeJson { triple, .. } | Self::Deserialize { triple, .. } => {
434 write!(f, "error deserializing custom target JSON for `{triple}`")
435 }
436 Self::Unavailable => {
437 write!(
438 f,
439 "custom platforms are currently unavailable: \
440 to enable them, add the `custom` feature to target-spec"
441 )
442 }
443 }
444 }
445}
446
447impl error::Error for CustomTripleCreateError {
448 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
449 match self {
450 #[cfg(feature = "custom")]
451 #[allow(deprecated)]
452 Self::DeserializeJson { error, .. } | Self::Deserialize { error, .. } => Some(error),
453 Self::Unavailable => None,
454 }
455 }
456}
457
458#[derive(Clone, Debug)]
462#[non_exhaustive]
463pub enum RustcVersionVerboseParseError {
464 InvalidUtf8(FromUtf8Error),
466
467 MissingHostLine {
469 output: String,
471 },
472}
473
474impl fmt::Display for RustcVersionVerboseParseError {
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476 match self {
477 RustcVersionVerboseParseError::InvalidUtf8(_) => {
478 write!(f, "output from `rustc -vV` was not valid UTF-8")
479 }
480 RustcVersionVerboseParseError::MissingHostLine { output } => {
481 write!(
482 f,
483 "output from `rustc -vV` did not contain a `host: ` line; output:\n---\n{}---",
484 output
485 )
486 }
487 }
488 }
489}
490
491impl error::Error for RustcVersionVerboseParseError {
492 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
493 match self {
494 RustcVersionVerboseParseError::InvalidUtf8(err) => Some(err),
495 RustcVersionVerboseParseError::MissingHostLine { .. } => None,
496 }
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use crate::{TargetSpecExpression, TargetSpecPlainString};
503 use test_case::test_case;
504
505 #[test_case("cfg()", 4..4; "empty expression results in span inside cfg")]
506 #[test_case("target_os = \"macos", 12..18; "unclosed quote specified without cfg")]
507 fn test_expression_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
508 let err = TargetSpecExpression::new(input).unwrap_err();
509 assert_eq!(err.span, expected_span);
510 }
511
512 #[test_case("foobar$", 6..7; "dollar sign at end of string")]
513 #[test_case("my🛑triple", 2..6; "multibyte character")]
514 fn test_plain_string_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
515 let err = TargetSpecPlainString::new(input.to_owned()).unwrap_err();
516 assert_eq!(err.span(), expected_span);
517 }
518}