use darling::Error as DarlingError;
use failure::{Error, Fail};
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::ToTokens;
use std::fmt;
use std::fmt::Display;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
#[derive(Debug, Fail)]
pub enum TracersError {
InvalidProvider {
message: String,
#[fail(cause)]
syn_error: Error,
},
SynError {
message: String,
#[fail(cause)]
syn_error: Error,
},
DarlingError {
message: String,
darling_error: Arc<Mutex<DarlingError>>,
},
InvalidCallExpression {
message: String,
#[fail(cause)]
syn_error: Error,
},
OtherError {
message: String,
#[fail(cause)]
error: Error,
},
MissingCallInBuildRs,
BuildInfoReadError {
message: String,
build_info_path: String,
#[fail(cause)]
error: Error,
},
BuildInfoWriteError {
message: String,
build_info_path: String,
#[fail(cause)]
error: Error,
},
ProviderTraitNotProcessedError {
message: String,
trait_name: String,
#[fail(cause)]
error: Error,
},
CodeGenerationError {
message: String,
},
NativeCodeGenerationError {
message: String,
#[fail(cause)]
error: Error,
},
}
impl Display for TracersError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TracersError::InvalidProvider { message, .. } => write!(f, "{}", message),
TracersError::SynError { message, .. } => write!(f, "{}", message),
TracersError::DarlingError { message, .. } => write!(f, "{}", message),
TracersError::InvalidCallExpression { message, .. } => write!(f, "{}", message),
TracersError::OtherError { message, .. } => write!(f, "{}", message),
TracersError::MissingCallInBuildRs => write!(f, "Build environment is incomplete; make sure you are calling `tracers_build::build()` in your `build.rs` build script"),
TracersError::BuildInfoReadError { message, .. } => write!(f, "{}", message),
TracersError::BuildInfoWriteError { message, .. } => write!(f, "{}", message),
TracersError::ProviderTraitNotProcessedError { message,.. } => write!(f, "{}", message),
TracersError::CodeGenerationError { message } => write!(f, "Error generating probing code: {}", message),
TracersError::NativeCodeGenerationError { message, .. } => write!(f, "Error generating or compiling native C++ wrapper code: {}", message)
}
}
}
unsafe impl Send for TracersError {}
unsafe impl Sync for TracersError {}
impl PartialEq<TracersError> for TracersError {
fn eq(&self, other: &TracersError) -> bool {
self.to_string() == other.to_string()
}
}
impl TracersError {
pub fn invalid_provider<T: ToTokens>(message: impl AsRef<str>, element: T) -> TracersError {
let message = format!(
"There's a problem with this provider trait: {}",
message.as_ref()
);
let e = Self::new_syn_error(&message, element);
TracersError::InvalidProvider {
message,
syn_error: e,
}
}
pub fn syn_error(message: impl AsRef<str>, e: syn::Error) -> TracersError {
let message = format!("Parse error: {}\nDetails: {}", message.as_ref(), e);
let e = syn::Error::new(e.span(), &message);
let e = Self::wrap_syn_error(e);
TracersError::SynError {
message,
syn_error: e,
}
}
pub fn syn_like_error<T: ToTokens, U: Display>(message: U, tokens: T) -> TracersError {
let message = message.to_string();
Self::syn_error(&message, syn::Error::new_spanned(tokens, &message))
}
pub fn darling_error(e: DarlingError) -> TracersError {
let message = e.to_string();
TracersError::DarlingError {
message,
darling_error: Arc::new(Mutex::new(e)),
}
}
pub fn invalid_call_expression<T: ToTokens>(
message: impl AsRef<str>,
element: T,
) -> TracersError {
let message = format!("Invalid call expression: {}", message.as_ref());
let e = Self::new_syn_error(&message, element);
TracersError::InvalidCallExpression {
message,
syn_error: e,
}
}
pub fn other_error<D: Display + Send + Sync + 'static>(e: failure::Context<D>) -> TracersError {
TracersError::OtherError {
message: Self::fail_string(&e),
error: e.into(),
}
}
pub fn missing_call_in_build_rs() -> TracersError {
TracersError::MissingCallInBuildRs
}
pub fn build_info_read_error(build_info_path: PathBuf, e: Error) -> TracersError {
let message = format!("Unable to read build info from '{}'.\nAre you sure you're calling `tracers_build::build()` in your `build.rs`?\nError cause: {}",
build_info_path.display(),
Self::error_string(&e));
TracersError::BuildInfoReadError {
message,
build_info_path: build_info_path.display().to_string(),
error: e,
}
}
pub fn build_info_write_error(build_info_path: PathBuf, e: Error) -> TracersError {
let message = format!("Unable to write build info from '{}'.\nAre you sure you're calling `tracers_build::build()` in your `build.rs`?\nError cause: {}",
build_info_path.display(),
Self::error_string(&e));
TracersError::BuildInfoWriteError {
message,
build_info_path: build_info_path.display().to_string(),
error: e,
}
}
pub fn provider_trait_not_processed_error<T: AsRef<str>, E: Into<Error> + Display>(
trait_name: T,
e: E,
) -> TracersError {
TracersError::ProviderTraitNotProcessedError {
message: format!("The provider trait '{}' couldn't be processed by the code generator: {}\nCheck your build output for errors. Tracing will be disabled for this provider",
trait_name.as_ref(),
e),
trait_name: trait_name.as_ref().to_owned(),
error: e.into()
}
}
pub fn code_generation_error<S: AsRef<str>>(message: S) -> TracersError {
TracersError::CodeGenerationError {
message: message.as_ref().to_owned(),
}
}
pub fn native_code_generation_error<S: AsRef<str>, E: Into<Error> + Display>(
message: S,
e: E,
) -> TracersError {
TracersError::NativeCodeGenerationError {
message: format!("{}: {}", message.as_ref(), e),
error: e.into(),
}
}
pub fn into_syn_error(self) -> syn::Error {
match self {
TracersError::InvalidProvider { syn_error, .. } => Self::error_as_syn_error(syn_error),
TracersError::SynError { syn_error, .. } => Self::error_as_syn_error(syn_error),
TracersError::InvalidCallExpression { syn_error, .. } => {
Self::error_as_syn_error(syn_error)
}
others => syn::Error::new(Span::call_site(), others.to_string()),
}
}
pub fn into_compiler_error(self) -> TokenStream {
if let TracersError::DarlingError { darling_error, .. } = self {
let lock =
Arc::try_unwrap(darling_error).expect("Somehow a lock is still held on this error");
let darling_error = lock
.into_inner()
.expect("The mutex can't possibly be locked");
darling_error.write_errors()
} else {
self.into_syn_error().to_compile_error()
}
}
fn new_syn_error<T: ToTokens, U: Display>(message: U, tokens: T) -> Error {
Self::wrap_syn_error(syn::Error::new_spanned(tokens, message))
}
fn wrap_syn_error(e: syn::Error) -> Error {
Self::wrap_error(e)
}
fn error_as_syn_error(e: Error) -> syn::Error {
e.downcast::<syn::Error>()
.unwrap_or_else(|e| syn::Error::new(Span::call_site(), e.to_string()))
}
fn wrap_error(e: impl std::error::Error + Sync + Send + 'static) -> Error {
e.into()
}
#[allow(clippy::redundant_closure)] fn error_string(e: &Error) -> String {
let causes: Vec<_> = e.iter_chain().map(|c| c.to_string()).collect();
causes.join(": ")
}
#[allow(clippy::redundant_closure)] fn fail_string(f: &dyn Fail) -> String {
let causes: Vec<_> = f.iter_chain().map(|c| c.to_string()).collect();
causes.join(": ")
}
}
impl<D: Display + Send + Sync + 'static> From<failure::Context<D>> for TracersError {
fn from(failure: failure::Context<D>) -> TracersError {
TracersError::other_error(failure)
}
}
pub type TracersResult<T> = std::result::Result<T, TracersError>;