use std::{borrow::Cow, error, fmt};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Error {
InvalidExpression(ExpressionParseError),
InvalidTargetSpecString(PlainStringParseError),
UnknownPlatformTriple(TripleParseError),
#[deprecated(
since = "3.3.0",
note = "this variant is no longer returned: instead, use CustomPlatformCreate"
)]
#[doc(hidden)]
CustomTripleCreate(CustomTripleCreateError),
CustomPlatformCreate(CustomTripleCreateError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidExpression(_) => write!(f, "invalid cfg() expression"),
Error::InvalidTargetSpecString(_) => {
write!(f, "failed to parse target spec as a plain string")
}
Error::UnknownPlatformTriple(_) => {
write!(f, "unknown platform triple")
}
#[allow(deprecated)]
Error::CustomTripleCreate(_) => write!(f, "error creating custom triple"),
Error::CustomPlatformCreate(_) => {
write!(f, "error creating custom platform")
}
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::InvalidExpression(err) => Some(err),
Error::InvalidTargetSpecString(err) => Some(err),
Error::UnknownPlatformTriple(err) => Some(err),
#[allow(deprecated)]
Error::CustomTripleCreate(err) => Some(err),
Error::CustomPlatformCreate(err) => Some(err),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ExpressionParseError {
pub input: String,
pub span: std::ops::Range<usize>,
pub kind: ExpressionParseErrorKind,
}
impl ExpressionParseError {
pub(crate) fn new(input: &str, error: cfg_expr::ParseError) -> Self {
let span = if input.starts_with("cfg(") && input.ends_with(')') {
(error.span.start + 4)..(error.span.end + 4)
} else {
error.span
};
Self {
input: input.to_owned(),
span,
kind: ExpressionParseErrorKind::from_cfg_expr(error.reason),
}
}
}
impl fmt::Display for ExpressionParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "error parsing cfg() expression")
}
}
impl error::Error for ExpressionParseError {}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExpressionParseErrorKind {
InvalidNot(usize),
InvalidCharacters,
UnclosedParens,
UnopenedParens,
UnclosedQuotes,
UnopenedQuotes,
Empty,
Unexpected {
expected: &'static [&'static str],
},
InvalidInteger,
MultipleRootPredicates,
InvalidHasAtomic,
UnknownBuiltin,
}
impl ExpressionParseErrorKind {
fn from_cfg_expr(reason: cfg_expr::error::Reason) -> Self {
use cfg_expr::error::Reason::*;
match reason {
InvalidCharacters => Self::InvalidCharacters,
UnclosedParens => Self::UnclosedParens,
UnopenedParens => Self::UnopenedParens,
UnclosedQuotes => Self::UnclosedQuotes,
UnopenedQuotes => Self::UnopenedQuotes,
Empty => Self::Empty,
Unexpected(expected) => Self::Unexpected { expected },
InvalidNot(np) => Self::InvalidNot(np),
InvalidInteger => Self::InvalidInteger,
MultipleRootPredicates => Self::MultipleRootPredicates,
InvalidHasAtomic => Self::InvalidHasAtomic,
UnknownBuiltin => Self::UnknownBuiltin,
}
}
}
impl fmt::Display for ExpressionParseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ExpressionParseErrorKind::*;
match self {
InvalidCharacters => f.write_str("invalid character(s)"),
UnclosedParens => f.write_str("unclosed parens"),
UnopenedParens => f.write_str("unopened parens"),
UnclosedQuotes => f.write_str("unclosed quotes"),
UnopenedQuotes => f.write_str("unopened quotes"),
Empty => f.write_str("empty expression"),
Unexpected { expected } => {
if expected.len() > 1 {
f.write_str("expected one of ")?;
for (i, exp) in expected.iter().enumerate() {
f.write_fmt(format_args!("{}`{exp}`", if i > 0 { ", " } else { "" }))?;
}
f.write_str(" here")
} else if !expected.is_empty() {
f.write_fmt(format_args!("expected a `{}` here", expected[0]))
} else {
f.write_str("the term was not expected here")
}
}
InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {np}")),
InvalidInteger => f.write_str("invalid integer"),
MultipleRootPredicates => f.write_str("multiple root predicates"),
InvalidHasAtomic => f.write_str("expected integer or \"ptr\""),
UnknownBuiltin => f.write_str("unknown built-in"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct PlainStringParseError {
pub input: String,
pub char_index: usize,
pub character: char,
}
impl PlainStringParseError {
pub(crate) fn new(input: String, char_index: usize, character: char) -> Self {
Self {
input,
char_index,
character,
}
}
pub fn span(&self) -> std::ops::Range<usize> {
let end = self.char_index + self.character.len_utf8();
self.char_index..end
}
}
impl fmt::Display for PlainStringParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"failed to parse `{}` at index {}: character \
must be alphanumeric, `-`, `_` or `.`",
self.input, self.char_index,
)
}
}
impl error::Error for PlainStringParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TripleParseError {
triple_str: Cow<'static, str>,
kind: TripleParseErrorKind,
}
impl TripleParseError {
pub(crate) fn new(
triple_str: Cow<'static, str>,
lexicon_err: cfg_expr::target_lexicon::ParseError,
) -> Self {
Self {
triple_str,
kind: TripleParseErrorKind::Lexicon(lexicon_err),
}
}
pub(crate) fn new_strict(triple_str: Cow<'static, str>) -> Self {
Self {
triple_str,
kind: TripleParseErrorKind::LexiconDisabled,
}
}
pub fn triple_str(&self) -> &str {
&self.triple_str
}
}
impl fmt::Display for TripleParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown triple string: {}", self.triple_str)
}
}
impl error::Error for TripleParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.kind)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
enum TripleParseErrorKind {
Lexicon(cfg_expr::target_lexicon::ParseError),
LexiconDisabled,
}
impl fmt::Display for TripleParseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Lexicon(_) => write!(
f,
"triple not in builtin platforms and heuristic parsing failed"
),
Self::LexiconDisabled => write!(
f,
"triple not in builtin platforms and heuristic parsing disabled"
),
}
}
}
impl error::Error for TripleParseErrorKind {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Lexicon(error) => Some(error),
Self::LexiconDisabled => None,
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum CustomTripleCreateError {
#[cfg(feature = "custom")]
#[deprecated(
since = "3.3.0",
note = "this variant is no longer returned: instead, \
use DeserializeJson which also includes the input string"
)]
#[doc(hidden)]
Deserialize {
triple: String,
error: std::sync::Arc<serde_json::Error>,
},
Unavailable,
#[cfg(feature = "custom")]
DeserializeJson {
triple: String,
input: String,
error: std::sync::Arc<serde_json::Error>,
},
}
impl CustomTripleCreateError {
#[inline]
pub fn input(&self) -> Option<&str> {
self.input_string().map(String::as_str)
}
pub fn input_string(&self) -> Option<&String> {
match self {
#[cfg(feature = "custom")]
Self::DeserializeJson { input, .. } => Some(input),
#[cfg(feature = "custom")]
#[allow(deprecated)]
Self::Deserialize { .. } => None,
Self::Unavailable => None,
}
}
#[inline]
pub fn line_and_column(&self) -> Option<(usize, usize)> {
match self {
#[cfg(feature = "custom")]
Self::DeserializeJson { error, .. } => Some((error.line(), error.column())),
#[cfg(feature = "custom")]
#[allow(deprecated)]
Self::Deserialize { .. } => None,
Self::Unavailable => None,
}
}
pub fn label(&self) -> Option<String> {
match self {
#[cfg(feature = "custom")]
Self::DeserializeJson { error, .. } => {
let label = error.to_string();
let trimmed = match label.rfind(" at line ") {
Some(idx) => label[..idx].to_string(),
None => label,
};
Some(trimmed)
}
#[cfg(feature = "custom")]
#[allow(deprecated)]
Self::Deserialize { .. } => None,
Self::Unavailable => None,
}
}
}
impl fmt::Display for CustomTripleCreateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "custom")]
#[allow(deprecated)]
Self::DeserializeJson { triple, .. } | Self::Deserialize { triple, .. } => {
write!(f, "error deserializing custom target JSON for `{triple}`")
}
Self::Unavailable => {
write!(
f,
"custom platforms are currently unavailable: \
to enable them, add the `custom` feature to target-spec"
)
}
}
}
}
impl error::Error for CustomTripleCreateError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
#[cfg(feature = "custom")]
#[allow(deprecated)]
Self::DeserializeJson { error, .. } | Self::Deserialize { error, .. } => Some(error),
Self::Unavailable => None,
}
}
}
#[cfg(test)]
mod tests {
use crate::{TargetSpecExpression, TargetSpecPlainString};
use test_case::test_case;
#[test_case("cfg()", 4..4; "empty expression results in span inside cfg")]
#[test_case("target_os = \"macos", 12..18; "unclosed quote specified without cfg")]
fn test_expression_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
let err = TargetSpecExpression::new(input).unwrap_err();
assert_eq!(err.span, expected_span);
}
#[test_case("foobar$", 6..7; "dollar sign at end of string")]
#[test_case("my🛑triple", 2..6; "multibyte character")]
fn test_plain_string_parse_error_span(input: &str, expected_span: std::ops::Range<usize>) {
let err = TargetSpecPlainString::new(input.to_owned()).unwrap_err();
assert_eq!(err.span(), expected_span);
}
}