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 CustomJsonUnavailable,
351
352 CustomCfgUnavailable,
359
360 #[deprecated(
363 since = "3.6.0",
364 note = "this variant is no longer returned: instead, \
365 use CustomJsonUnavailable or CustomCfgUnavailable"
366 )]
367 #[doc(hidden)]
368 Unavailable,
369
370 #[cfg(feature = "custom")]
371 DeserializeJson {
373 triple: String,
375
376 input: String,
378
379 error: std::sync::Arc<serde_json::Error>,
381 },
382
383 #[cfg(feature = "custom-cfg")]
384 ParseCfg {
386 triple: String,
388
389 input: String,
391
392 message: String,
394
395 line: usize,
397 },
398
399 ConflictingCustomPlatformSources {
402 triple: String,
404 },
405}
406
407impl CustomTripleCreateError {
408 #[inline]
410 pub fn input(&self) -> Option<&str> {
411 self.input_string().map(String::as_str)
412 }
413
414 pub fn input_string(&self) -> Option<&String> {
420 match self {
421 #[cfg(feature = "custom")]
422 Self::DeserializeJson { input, .. } => Some(input),
423 #[cfg(feature = "custom")]
424 #[allow(deprecated)]
425 Self::Deserialize { .. } => None,
426 #[cfg(feature = "custom-cfg")]
427 Self::ParseCfg { input, .. } => Some(input),
428 Self::CustomJsonUnavailable
429 | Self::CustomCfgUnavailable
430 | Self::ConflictingCustomPlatformSources { .. } => None,
431 #[allow(deprecated)]
432 Self::Unavailable => None,
433 }
434 }
435
436 #[inline]
442 pub fn line_and_column(&self) -> Option<(usize, usize)> {
443 match self {
444 #[cfg(feature = "custom")]
445 Self::DeserializeJson { error, .. } => Some((error.line(), error.column())),
446 #[cfg(feature = "custom")]
447 #[allow(deprecated)]
448 Self::Deserialize { .. } => None,
449 #[cfg(feature = "custom-cfg")]
450 Self::ParseCfg { line, .. } => Some((*line, 0)),
451 Self::CustomJsonUnavailable
452 | Self::CustomCfgUnavailable
453 | Self::ConflictingCustomPlatformSources { .. } => None,
454 #[allow(deprecated)]
455 Self::Unavailable => None,
456 }
457 }
458
459 pub fn label(&self) -> Option<String> {
464 match self {
465 #[cfg(feature = "custom")]
466 Self::DeserializeJson { error, .. } => {
467 let label = error.to_string();
468 let trimmed = match label.rfind(" at line ") {
471 Some(idx) => label[..idx].to_string(),
472 None => label,
473 };
474 Some(trimmed)
475 }
476 #[cfg(feature = "custom")]
477 #[allow(deprecated)]
478 Self::Deserialize { .. } => None,
479 #[cfg(feature = "custom-cfg")]
480 Self::ParseCfg { message, .. } => Some(message.clone()),
481 Self::CustomJsonUnavailable
482 | Self::CustomCfgUnavailable
483 | Self::ConflictingCustomPlatformSources { .. } => None,
484 #[allow(deprecated)]
485 Self::Unavailable => None,
486 }
487 }
488}
489
490impl fmt::Display for CustomTripleCreateError {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 match self {
493 #[cfg(feature = "custom")]
494 #[allow(deprecated)]
495 Self::DeserializeJson { triple, .. } | Self::Deserialize { triple, .. } => {
496 write!(f, "error deserializing custom target JSON for `{triple}`")
497 }
498 #[cfg(feature = "custom-cfg")]
499 Self::ParseCfg { triple, .. } => {
500 write!(f, "error parsing `rustc --print=cfg` output for `{triple}`")
501 }
502 Self::CustomJsonUnavailable => {
503 write!(
504 f,
505 "custom JSON platforms are currently unavailable: \
506 to enable them, add the `custom` feature \
507 to target-spec"
508 )
509 }
510 Self::CustomCfgUnavailable => {
511 write!(
512 f,
513 "custom cfg platforms are currently unavailable: \
514 to enable them, add the `custom-cfg` \
515 feature to target-spec"
516 )
517 }
518 Self::ConflictingCustomPlatformSources { triple } => {
519 write!(
520 f,
521 "conflicting custom platform sources for \
522 `{triple}`: both `custom_json` and \
523 `custom_cfg` are specified, but only one \
524 is allowed"
525 )
526 }
527 #[allow(deprecated)]
528 Self::Unavailable => {
529 write!(
530 f,
531 "custom platforms are currently unavailable: \
532 to enable them, add the `custom` feature to target-spec"
533 )
534 }
535 }
536 }
537}
538
539impl error::Error for CustomTripleCreateError {
540 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
541 match self {
542 #[cfg(feature = "custom")]
543 #[allow(deprecated)]
544 Self::DeserializeJson { error, .. } | Self::Deserialize { error, .. } => Some(error),
545 #[cfg(feature = "custom-cfg")]
546 Self::ParseCfg { .. } => None,
547 Self::CustomJsonUnavailable
548 | Self::CustomCfgUnavailable
549 | Self::ConflictingCustomPlatformSources { .. } => None,
550 #[allow(deprecated)]
551 Self::Unavailable => None,
552 }
553 }
554}
555
556#[derive(Clone, Debug)]
560#[non_exhaustive]
561pub enum RustcVersionVerboseParseError {
562 InvalidUtf8(FromUtf8Error),
564
565 MissingHostLine {
567 output: String,
569 },
570}
571
572impl fmt::Display for RustcVersionVerboseParseError {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 match self {
575 RustcVersionVerboseParseError::InvalidUtf8(_) => {
576 write!(f, "output from `rustc -vV` was not valid UTF-8")
577 }
578 RustcVersionVerboseParseError::MissingHostLine { output } => {
579 write!(
580 f,
581 "output from `rustc -vV` did not contain a `host: ` line; output:\n---\n{output}---"
582 )
583 }
584 }
585 }
586}
587
588impl error::Error for RustcVersionVerboseParseError {
589 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
590 match self {
591 RustcVersionVerboseParseError::InvalidUtf8(err) => Some(err),
592 RustcVersionVerboseParseError::MissingHostLine { .. } => None,
593 }
594 }
595}
596
597#[cfg(test)]
598mod tests {
599 use crate::{TargetSpecExpression, TargetSpecPlainString};
600 use test_case::test_case;
601
602 #[test_case("cfg()", 4..4; "empty expression results in span inside cfg")]
603 #[test_case("target_os = \"macos", 12..18; "unclosed quote specified without cfg")]
604 fn test_expression_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
605 let err = TargetSpecExpression::new(input).unwrap_err();
606 assert_eq!(err.span, expected_span);
607 }
608
609 #[test_case("foobar$", 6..7; "dollar sign at end of string")]
610 #[test_case("my🛑triple", 2..6; "multibyte character")]
611 fn test_plain_string_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
612 let err = TargetSpecPlainString::new(input.to_owned()).unwrap_err();
613 assert_eq!(err.span(), expected_span);
614 }
615}