use proc_macro2::{Span, TokenStream};
use std::error::Error as StdError;
use std::fmt;
use std::iter::{self, Iterator};
use std::string::ToString;
use std::vec;
use syn::spanned::Spanned;
use syn::{Lit, LitStr, Path};
mod kind;
use crate::util::path_to_string;
use self::kind::{ErrorKind, ErrorUnknownField};
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug, Clone)]
pub struct Error {
kind: ErrorKind,
locations: Vec<String>,
span: Option<Span>,
}
impl Error {
pub(in crate::error) fn new(kind: ErrorKind) -> Self {
Error {
kind,
locations: Vec::new(),
span: None,
}
}
pub fn custom<T: fmt::Display>(msg: T) -> Self {
Error::new(ErrorKind::Custom(msg.to_string()))
}
pub fn duplicate_field(name: &str) -> Self {
Error::new(ErrorKind::DuplicateField(name.into()))
}
pub fn duplicate_field_path(path: &Path) -> Self {
Error::duplicate_field(&path_to_string(path))
}
pub fn missing_field(name: &str) -> Self {
Error::new(ErrorKind::MissingField(name.into()))
}
pub fn unknown_field(name: &str) -> Self {
Error::new(ErrorKind::UnknownField(name.into()))
}
pub fn unknown_field_path(path: &Path) -> Self {
Error::unknown_field(&path_to_string(path))
}
pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
where
T: AsRef<str> + 'a,
I: IntoIterator<Item = &'a T>,
{
Error::new(ErrorUnknownField::with_alts(field, alternates).into())
}
pub fn unsupported_shape(shape: &str) -> Self {
Error::new(ErrorKind::UnsupportedShape(shape.into()))
}
pub fn unsupported_format(format: &str) -> Self {
Error::new(ErrorKind::UnexpectedFormat(format.into()))
}
pub fn unexpected_type(ty: &str) -> Self {
Error::new(ErrorKind::UnexpectedType(ty.into()))
}
pub fn unexpected_lit_type(lit: &Lit) -> Self {
Error::unexpected_type(match *lit {
Lit::Str(_) => "string",
Lit::ByteStr(_) => "byte string",
Lit::Byte(_) => "byte",
Lit::Char(_) => "char",
Lit::Int(_) => "int",
Lit::Float(_) => "float",
Lit::Bool(_) => "bool",
Lit::Verbatim(_) => "verbatim",
})
.with_span(lit)
}
pub fn unknown_value(value: &str) -> Self {
Error::new(ErrorKind::UnknownValue(value.into()))
}
pub fn too_few_items(min: usize) -> Self {
Error::new(ErrorKind::TooFewItems(min))
}
pub fn too_many_items(max: usize) -> Self {
Error::new(ErrorKind::TooManyItems(max))
}
pub fn multiple(mut errors: Vec<Error>) -> Self {
match errors.len() {
1 => errors
.pop()
.expect("Error array of length 1 has a first item"),
0 => panic!("Can't deal with 0 errors"),
_ => Error::new(ErrorKind::Multiple(errors)),
}
}
pub fn accumulator() -> Accumulator {
Default::default()
}
}
impl Error {
pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
Error::unknown_value(&value.value()).with_span(value)
}
}
#[allow(clippy::len_without_is_empty)] impl Error {
pub fn has_span(&self) -> bool {
self.span.is_some()
}
pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
if !self.has_span() {
self.span = Some(node.span());
}
self
}
pub fn span(&self) -> Span {
self.span.unwrap_or_else(Span::call_site)
}
pub fn explicit_span(&self) -> Option<Span> {
self.span
}
pub fn flatten(self) -> Self {
Error::multiple(self.into_vec())
}
fn into_vec(self) -> Vec<Self> {
if let ErrorKind::Multiple(errors) = self.kind {
let mut flat = Vec::new();
for error in errors {
flat.extend(error.prepend_at(self.locations.clone()).into_vec());
}
flat
} else {
vec![self]
}
}
pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
self.locations.insert(0, location.to_string());
self
}
pub fn at_path(self, path: &Path) -> Self {
self.at(path_to_string(path))
}
pub fn len(&self) -> usize {
self.kind.len()
}
fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
if !locations.is_empty() {
locations.extend(self.locations);
self.locations = locations;
}
self
}
#[cfg(test)]
pub(crate) fn location(&self) -> Vec<&str> {
self.locations.iter().map(|i| i.as_str()).collect()
}
pub fn write_errors(self) -> TokenStream {
#[cfg(feature = "diagnostics")]
{
self.emit();
quote!()
}
#[cfg(not(feature = "diagnostics"))]
{
syn::Error::from(self).into_compile_error()
}
}
#[cfg(feature = "diagnostics")]
fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
use proc_macro::{Diagnostic, Level};
match self.kind {
ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
_ => match self.span {
Some(span) => span.unwrap().error(self.kind.to_string()),
None => Diagnostic::new(Level::Error, self.to_string()),
},
}
}
#[cfg(feature = "diagnostics")]
pub fn emit(self) {
for error in self.flatten() {
error.single_to_diagnostic().emit()
}
}
#[cfg(feature = "diagnostics")]
#[allow(dead_code)]
fn emit_with_macro_help_span(self) {
use proc_macro::Diagnostic;
for error in self.flatten() {
let needs_help = error.has_span();
let diagnostic = error.single_to_diagnostic();
Diagnostic::emit(if needs_help {
diagnostic.span_help(
Span::call_site().unwrap(),
"Encountered as part of this derive-mode-macro",
)
} else {
diagnostic
})
}
}
}
impl StdError for Error {
fn description(&self) -> &str {
self.kind.description()
}
fn cause(&self) -> Option<&dyn StdError> {
None
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.kind)?;
if !self.locations.is_empty() {
write!(f, " at {}", self.locations.join("/"))?;
}
Ok(())
}
}
impl From<syn::Error> for Error {
fn from(e: syn::Error) -> Self {
Self {
span: Some(e.span()),
..Self::custom(e)
}
}
}
impl From<Error> for syn::Error {
fn from(e: Error) -> Self {
if e.len() == 1 {
if let Some(span) = e.explicit_span() {
syn::Error::new(span, e.kind)
} else {
syn::Error::new(e.span(), e)
}
} else {
let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
let mut error = syn_errors
.next()
.expect("darling::Error can never be empty");
for next_error in syn_errors {
error.combine(next_error);
}
error
}
}
}
#[cfg(test)]
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.locations == other.locations
}
}
#[cfg(test)]
impl Eq for Error {}
impl IntoIterator for Error {
type Item = Error;
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
if let ErrorKind::Multiple(errors) = self.kind {
IntoIter {
inner: IntoIterEnum::Multiple(errors.into_iter()),
}
} else {
IntoIter {
inner: IntoIterEnum::Single(iter::once(self)),
}
}
}
}
enum IntoIterEnum {
Single(iter::Once<Error>),
Multiple(vec::IntoIter<Error>),
}
impl Iterator for IntoIterEnum {
type Item = Error;
fn next(&mut self) -> Option<Self::Item> {
match *self {
IntoIterEnum::Single(ref mut content) => content.next(),
IntoIterEnum::Multiple(ref mut content) => content.next(),
}
}
}
pub struct IntoIter {
inner: IntoIterEnum,
}
impl Iterator for IntoIter {
type Item = Error;
fn next(&mut self) -> Option<Error> {
self.inner.next()
}
}
#[derive(Debug)]
#[must_use = "Accumulator will panic on drop if not defused."]
pub struct Accumulator(Option<Vec<Error>>);
impl Accumulator {
pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
self.handle(f())
}
pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
match result {
Ok(y) => Some(y),
Err(e) => {
self.push(e);
None
}
}
}
pub fn finish(self) -> Result<()> {
self.finish_with(())
}
pub fn finish_with<T>(self, success: T) -> Result<T> {
let errors = self.into_inner();
if errors.is_empty() {
Ok(success)
} else {
Err(Error::multiple(errors))
}
}
fn errors(&mut self) -> &mut Vec<Error> {
match &mut self.0 {
Some(errors) => errors,
None => panic!("darling internal error: Accumulator accessed after defuse"),
}
}
#[must_use = "Accumulated errors should be handled or propagated to the caller"]
pub fn into_inner(mut self) -> Vec<Error> {
match std::mem::replace(&mut self.0, None) {
Some(errors) => errors,
None => panic!("darling internal error: Accumulator accessed after defuse"),
}
}
pub fn push(&mut self, error: Error) {
self.errors().push(error)
}
pub fn checkpoint(self) -> Result<Accumulator> {
self.finish()?;
Ok(Self::default())
}
}
impl Default for Accumulator {
fn default() -> Self {
Accumulator(Some(vec![]))
}
}
impl Extend<Error> for Accumulator {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Error>,
{
self.errors().extend(iter)
}
}
impl Drop for Accumulator {
fn drop(&mut self) {
if !std::thread::panicking() {
if let Some(errors) = &mut self.0 {
match errors.len() {
0 => panic!("darling::error::Accumulator dropped without being finished"),
error_count => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn flatten_noop() {
let err = Error::duplicate_field("hello").at("world");
assert_eq!(err.clone().flatten(), err);
}
#[test]
fn flatten_simple() {
let err = Error::multiple(vec![
Error::unknown_field("hello").at("world"),
Error::missing_field("hell_no").at("world"),
])
.at("foo")
.flatten();
assert!(err.location().is_empty());
let mut err_iter = err.into_iter();
let first = err_iter.next();
assert!(first.is_some());
assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
let second = err_iter.next();
assert!(second.is_some());
assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
assert!(err_iter.next().is_none());
}
#[test]
fn len_single() {
let err = Error::duplicate_field("hello");
assert_eq!(1, err.len());
}
#[test]
fn len_multiple() {
let err = Error::multiple(vec![
Error::duplicate_field("hello"),
Error::missing_field("hell_no"),
]);
assert_eq!(2, err.len());
}
#[test]
fn len_nested() {
let err = Error::multiple(vec![
Error::duplicate_field("hello"),
Error::multiple(vec![
Error::duplicate_field("hi"),
Error::missing_field("bye"),
Error::multiple(vec![Error::duplicate_field("whatsup")]),
]),
]);
assert_eq!(4, err.len());
}
#[test]
fn accum_ok() {
let errs = Error::accumulator();
assert_eq!("test", errs.finish_with("test").unwrap());
}
#[test]
fn accum_errr() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
errs.finish().unwrap_err();
}
#[test]
fn accum_into_inner() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
let errs: Vec<_> = errs.into_inner();
assert_eq!(errs.len(), 1);
}
#[test]
#[should_panic(expected = "Accumulator dropped")]
fn accum_drop_panic() {
let _errs = Error::accumulator();
}
#[test]
#[should_panic(expected = "2 errors")]
fn accum_drop_panic_with_error_count() {
let mut errors = Error::accumulator();
errors.push(Error::custom("first"));
errors.push(Error::custom("second"));
}
#[test]
fn accum_checkpoint_error() {
let mut errs = Error::accumulator();
errs.push(Error::custom("foo!"));
errs.checkpoint().unwrap_err();
}
#[test]
#[should_panic(expected = "Accumulator dropped")]
fn accum_checkpoint_drop_panic() {
let mut errs = Error::accumulator();
errs = errs.checkpoint().unwrap();
let _ = errs;
}
}