1//! The `darling::Error` type, the multiple error `Accumulator`, and their internals.
2//!
3//! Error handling is one of the core values of `darling`; creating great errors is hard and
4//! never the reason that a proc-macro author started writing their crate. As a result, the
5//! `Error` type in `darling` tries to make adding span information, suggestions, and other
6//! help content easy when manually implementing `darling` traits, and automatic when deriving
7//! them.
8
9use proc_macro2::{Span, TokenStream};
10use std::error::Error as StdError;
11use std::fmt;
12use std::iter::{self, Iterator};
13use std::string::ToString;
14use std::vec;
15use syn::spanned::Spanned;
16use syn::{Expr, Lit, LitStr, Path};
17
18#[cfg(feature = "diagnostics")]
19mod child;
20mod kind;
21
22use crate::util::path_to_string;
23
24use self::kind::{ErrorKind, ErrorUnknownField};
25
26/// An alias of `Result` specific to attribute parsing.
27pub type Result<T> = ::std::result::Result<T, Error>;
28
29/// An error encountered during attribute parsing.
30///
31/// Given that most errors darling encounters represent code bugs in dependent crates,
32/// the internal structure of the error is deliberately opaque.
33///
34/// # Usage
35/// Proc-macro expansion happens very infrequently compared to runtime tasks such as
36/// deserialization, and it happens in the context of an expensive compilation taks.
37/// For that reason, darling prefers not to fail on the first error it encounters, instead
38/// doing as much work as it can, accumulating errors into a single report.
39///
40/// As a result, `darling::Error` is more of guaranteed-non-empty error collection
41/// than a single problem. These errors also have some notion of hierarchy, stemming from
42/// the hierarchical nature of darling's input.
43///
44/// These characteristics make for great experiences when using darling-powered crates,
45/// provided crates using darling adhere to some best practices:
46///
47/// 1. Do not attempt to simplify a `darling::Error` into some other error type, such as
48/// `syn::Error`. To surface compile errors, instead use `darling::Error::write_errors`.
49/// This preserves all span information, suggestions, etc. Wrapping a `darling::Error` in
50/// a custom error enum works as-expected and does not force any loss of fidelity.
51/// 2. Do not use early return (e.g. the `?` operator) for custom validations. Instead,
52/// create an [`error::Accumulator`](Accumulator) to collect errors as they are encountered. Then use
53/// [`Accumulator::finish`] to return your validated result; it will give `Ok` if and only if
54/// no errors were encountered. This can create very complex custom validation functions;
55/// in those cases, split independent "validation chains" out into their own functions to
56/// keep the main validator manageable.
57/// 3. Use `darling::Error::custom` to create additional errors as-needed, then call `with_span`
58/// to ensure those errors appear in the right place. Use `darling::util::SpannedValue` to keep
59/// span information around on parsed fields so that custom diagnostics can point to the correct
60/// parts of the input AST.
61#[derive(Debug, Clone)]
62pub struct Error {
63 kind: ErrorKind,
64 locations: Vec<String>,
65 /// The span to highlight in the emitted diagnostic.
66 span: Option<Span>,
67 /// Additional diagnostic messages to show with the error.
68 #[cfg(feature = "diagnostics")]
69 children: Vec<child::ChildDiagnostic>,
70}
71
72/// Error creation functions
73impl Error {
74 pub(in crate::error) fn new(kind: ErrorKind) -> Self {
75 Error {
76 kind,
77 locations: Vec::new(),
78 span: None,
79 #[cfg(feature = "diagnostics")]
80 children: vec![],
81 }
82 }
83
84 /// Creates a new error with a custom message.
85 pub fn custom<T: fmt::Display>(msg: T) -> Self {
86 Error::new(ErrorKind::Custom(msg.to_string()))
87 }
88
89 /// Creates a new error for a field that appears twice in the input.
90 pub fn duplicate_field(name: &str) -> Self {
91 Error::new(ErrorKind::DuplicateField(name.into()))
92 }
93
94 /// Creates a new error for a field that appears twice in the input. Helper to avoid repeating
95 /// the syn::Path to String conversion.
96 pub fn duplicate_field_path(path: &Path) -> Self {
97 Error::duplicate_field(&path_to_string(path))
98 }
99
100 /// Creates a new error for a non-optional field that does not appear in the input.
101 pub fn missing_field(name: &str) -> Self {
102 Error::new(ErrorKind::MissingField(name.into()))
103 }
104
105 /// Creates a new error for a field name that appears in the input but does not correspond
106 /// to a known field.
107 pub fn unknown_field(name: &str) -> Self {
108 Error::new(ErrorKind::UnknownField(name.into()))
109 }
110
111 /// Creates a new error for a field name that appears in the input but does not correspond
112 /// to a known field. Helper to avoid repeating the syn::Path to String conversion.
113 pub fn unknown_field_path(path: &Path) -> Self {
114 Error::unknown_field(&path_to_string(path))
115 }
116
117 /// Creates a new error for a field name that appears in the input but does not correspond to
118 /// a known attribute. The second argument is the list of known attributes; if a similar name
119 /// is found that will be shown in the emitted error message.
120 pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
121 where
122 T: AsRef<str> + 'a,
123 I: IntoIterator<Item = &'a T>,
124 {
125 Error::new(ErrorUnknownField::with_alts(field, alternates).into())
126 }
127
128 /// Creates a new error for a struct or variant that does not adhere to the supported shape.
129 pub fn unsupported_shape(shape: &str) -> Self {
130 Error::new(ErrorKind::UnsupportedShape {
131 observed: shape.into(),
132 expected: None,
133 })
134 }
135
136 pub fn unsupported_shape_with_expected<T: fmt::Display>(shape: &str, expected: &T) -> Self {
137 Error::new(ErrorKind::UnsupportedShape {
138 observed: shape.into(),
139 expected: Some(expected.to_string()),
140 })
141 }
142
143 pub fn unsupported_format(format: &str) -> Self {
144 Error::new(ErrorKind::UnexpectedFormat(format.into()))
145 }
146
147 /// Creates a new error for a field which has an unexpected literal type.
148 pub fn unexpected_type(ty: &str) -> Self {
149 Error::new(ErrorKind::UnexpectedType(ty.into()))
150 }
151
152 pub fn unexpected_expr_type(expr: &Expr) -> Self {
153 Error::unexpected_type(match *expr {
154 Expr::Array(_) => "array",
155 Expr::Assign(_) => "assign",
156 Expr::Async(_) => "async",
157 Expr::Await(_) => "await",
158 Expr::Binary(_) => "binary",
159 Expr::Block(_) => "block",
160 Expr::Break(_) => "break",
161 Expr::Call(_) => "call",
162 Expr::Cast(_) => "cast",
163 Expr::Closure(_) => "closure",
164 Expr::Const(_) => "const",
165 Expr::Continue(_) => "continue",
166 Expr::Field(_) => "field",
167 Expr::ForLoop(_) => "for_loop",
168 Expr::Group(_) => "group",
169 Expr::If(_) => "if",
170 Expr::Index(_) => "index",
171 Expr::Infer(_) => "infer",
172 Expr::Let(_) => "let",
173 Expr::Lit(_) => "lit",
174 Expr::Loop(_) => "loop",
175 Expr::Macro(_) => "macro",
176 Expr::Match(_) => "match",
177 Expr::MethodCall(_) => "method_call",
178 Expr::Paren(_) => "paren",
179 Expr::Path(_) => "path",
180 Expr::Range(_) => "range",
181 Expr::Reference(_) => "reference",
182 Expr::Repeat(_) => "repeat",
183 Expr::Return(_) => "return",
184 Expr::Struct(_) => "struct",
185 Expr::Try(_) => "try",
186 Expr::TryBlock(_) => "try_block",
187 Expr::Tuple(_) => "tuple",
188 Expr::Unary(_) => "unary",
189 Expr::Unsafe(_) => "unsafe",
190 Expr::Verbatim(_) => "verbatim",
191 Expr::While(_) => "while",
192 Expr::Yield(_) => "yield",
193 // non-exhaustive enum
194 _ => "unknown",
195 })
196 .with_span(expr)
197 }
198
199 /// Creates a new error for a field which has an unexpected literal type. This will automatically
200 /// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
201 /// literal value.
202 ///
203 /// # Usage
204 /// This is most frequently used in overrides of the `FromMeta::from_value` method.
205 ///
206 /// ```rust
207 /// # // pretend darling_core is darling so the doc example looks correct.
208 /// # extern crate darling_core as darling;
209 /// # extern crate syn;
210 ///
211 /// use darling::{FromMeta, Error, Result};
212 /// use syn::{Lit, LitStr};
213 ///
214 /// pub struct Foo(String);
215 ///
216 /// impl FromMeta for Foo {
217 /// fn from_value(value: &Lit) -> Result<Self> {
218 /// if let Lit::Str(ref lit_str) = *value {
219 /// Ok(Foo(lit_str.value()))
220 /// } else {
221 /// Err(Error::unexpected_lit_type(value))
222 /// }
223 /// }
224 /// }
225 ///
226 /// # fn main() {}
227 /// ```
228 pub fn unexpected_lit_type(lit: &Lit) -> Self {
229 Error::unexpected_type(match *lit {
230 Lit::Str(_) => "string",
231 Lit::ByteStr(_) => "byte string",
232 Lit::Byte(_) => "byte",
233 Lit::Char(_) => "char",
234 Lit::Int(_) => "int",
235 Lit::Float(_) => "float",
236 Lit::Bool(_) => "bool",
237 Lit::Verbatim(_) => "verbatim",
238 // non-exhaustive enum
239 _ => "unknown",
240 })
241 .with_span(lit)
242 }
243
244 /// Creates a new error for a value which doesn't match a set of expected literals.
245 pub fn unknown_value(value: &str) -> Self {
246 Error::new(ErrorKind::UnknownValue(value.into()))
247 }
248
249 /// Creates a new error for a list which did not get enough items to proceed.
250 pub fn too_few_items(min: usize) -> Self {
251 Error::new(ErrorKind::TooFewItems(min))
252 }
253
254 /// Creates a new error when a list got more items than it supports. The `max` argument
255 /// is the largest number of items the receiver could accept.
256 pub fn too_many_items(max: usize) -> Self {
257 Error::new(ErrorKind::TooManyItems(max))
258 }
259
260 /// Bundle a set of multiple errors into a single `Error` instance.
261 ///
262 /// Usually it will be more convenient to use an [`error::Accumulator`](Accumulator).
263 ///
264 /// # Panics
265 /// This function will panic if `errors.is_empty() == true`.
266 pub fn multiple(mut errors: Vec<Error>) -> Self {
267 match errors.len() {
268 1 => errors
269 .pop()
270 .expect("Error array of length 1 has a first item"),
271 0 => panic!("Can't deal with 0 errors"),
272 _ => Error::new(ErrorKind::Multiple(errors)),
273 }
274 }
275
276 /// Creates an error collector, for aggregating multiple errors
277 ///
278 /// See [`Accumulator`] for details.
279 pub fn accumulator() -> Accumulator {
280 Default::default()
281 }
282}
283
284impl Error {
285 /// Create a new error about a literal string that doesn't match a set of known
286 /// or permissible values. This function can be made public if the API proves useful
287 /// beyond impls for `syn` types.
288 pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
289 Error::unknown_value(&value.value()).with_span(node:value)
290 }
291}
292
293/// Error instance methods
294#[allow(clippy::len_without_is_empty)] // Error can never be empty
295impl Error {
296 /// Check if this error is associated with a span in the token stream.
297 pub fn has_span(&self) -> bool {
298 self.span.is_some()
299 }
300
301 /// Tie a span to the error if none is already present. This is used in `darling::FromMeta`
302 /// and other traits to attach errors to the most specific possible location in the input
303 /// source code.
304 ///
305 /// All `darling`-built impls, either from the crate or from the proc macro, will call this
306 /// when appropriate during parsing, so it should not be necessary to call this unless you have
307 /// overridden:
308 ///
309 /// * `FromMeta::from_meta`
310 /// * `FromMeta::from_nested_meta`
311 /// * `FromMeta::from_value`
312 pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
313 if !self.has_span() {
314 self.span = Some(node.span());
315 }
316
317 self
318 }
319
320 /// Get a span for the error.
321 ///
322 /// # Return Value
323 /// This function will return [`Span::call_site()`](proc_macro2::Span) if [`Self::has_span`] is `false`.
324 /// To get the span only if one has been explicitly set for `self`, instead use [`Error::explicit_span`].
325 pub fn span(&self) -> Span {
326 self.span.unwrap_or_else(Span::call_site)
327 }
328
329 /// Get the span for `self`, if one has been set.
330 pub fn explicit_span(&self) -> Option<Span> {
331 self.span
332 }
333
334 /// Recursively converts a tree of errors to a flattened list.
335 ///
336 /// # Child Diagnostics
337 /// If the `diagnostics` feature is enabled, any child diagnostics on `self`
338 /// will be cloned down to all the errors within `self`.
339 pub fn flatten(self) -> Self {
340 Error::multiple(self.into_vec())
341 }
342
343 fn into_vec(self) -> Vec<Self> {
344 if let ErrorKind::Multiple(errors) = self.kind {
345 let locations = self.locations;
346
347 #[cfg(feature = "diagnostics")]
348 let children = self.children;
349
350 errors
351 .into_iter()
352 .flat_map(|error| {
353 // This is mutated if the diagnostics feature is enabled
354 #[allow(unused_mut)]
355 let mut error = error.prepend_at(locations.clone());
356
357 // Any child diagnostics in `self` are cloned down to all the distinct
358 // errors contained in `self`.
359 #[cfg(feature = "diagnostics")]
360 error.children.extend(children.iter().cloned());
361
362 error.into_vec()
363 })
364 .collect()
365 } else {
366 vec![self]
367 }
368 }
369
370 /// Adds a location to the error, such as a field or variant.
371 /// Locations must be added in reverse order of specificity.
372 pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
373 self.locations.insert(0, location.to_string());
374 self
375 }
376
377 /// Adds a location to the error, such as a field or variant.
378 /// Locations must be added in reverse order of specificity. This is a helper function to avoid
379 /// repeating path to string logic.
380 pub fn at_path(self, path: &Path) -> Self {
381 self.at(path_to_string(path))
382 }
383
384 /// Gets the number of individual errors in this error.
385 ///
386 /// This function never returns `0`, as it's impossible to construct
387 /// a multi-error from an empty `Vec`.
388 pub fn len(&self) -> usize {
389 self.kind.len()
390 }
391
392 /// Adds a location chain to the head of the error's existing locations.
393 fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
394 if !locations.is_empty() {
395 locations.extend(self.locations);
396 self.locations = locations;
397 }
398
399 self
400 }
401
402 /// Gets the location slice.
403 #[cfg(test)]
404 pub(crate) fn location(&self) -> Vec<&str> {
405 self.locations.iter().map(|i| i.as_str()).collect()
406 }
407
408 /// Write this error and any children as compile errors into a `TokenStream` to
409 /// be returned by the proc-macro.
410 ///
411 /// The behavior of this method will be slightly different if the `diagnostics` feature
412 /// is enabled: In that case, the diagnostics will be emitted immediately by this call,
413 /// and an empty `TokenStream` will be returned.
414 ///
415 /// Return these tokens unmodified to avoid disturbing the attached span information.
416 ///
417 /// # Usage
418 /// ```rust,ignore
419 /// // in your proc-macro function
420 /// let opts = match MyOptions::from_derive_input(&ast) {
421 /// Ok(val) => val,
422 /// Err(err) => {
423 /// return err.write_errors();
424 /// }
425 /// }
426 /// ```
427 pub fn write_errors(self) -> TokenStream {
428 #[cfg(feature = "diagnostics")]
429 {
430 self.emit();
431 TokenStream::default()
432 }
433
434 #[cfg(not(feature = "diagnostics"))]
435 {
436 syn::Error::from(self).into_compile_error()
437 }
438 }
439
440 #[cfg(feature = "diagnostics")]
441 fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
442 use proc_macro::{Diagnostic, Level};
443
444 // Delegate to dedicated error formatters when applicable.
445 //
446 // If span information is available, don't include the error property path
447 // since it's redundant and not consistent with native compiler diagnostics.
448 let diagnostic = match self.kind {
449 ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
450 _ => match self.span {
451 Some(span) => span.unwrap().error(self.kind.to_string()),
452 None => Diagnostic::new(Level::Error, self.to_string()),
453 },
454 };
455
456 self.children
457 .into_iter()
458 .fold(diagnostic, |out, child| child.append_to(out))
459 }
460
461 /// Transform this error and its children into a list of compiler diagnostics
462 /// and emit them. If the `Error` has associated span information, the diagnostics
463 /// will identify the correct location in source code automatically.
464 ///
465 /// # Stability
466 /// This is only available on `nightly` until the compiler `proc_macro_diagnostic`
467 /// feature stabilizes. Until then, it may break at any time.
468 #[cfg(feature = "diagnostics")]
469 pub fn emit(self) {
470 for error in self.flatten() {
471 error.single_to_diagnostic().emit()
472 }
473 }
474
475 /// Transform the error into a compiler diagnostic and - if the diagnostic points to
476 /// a specific code location - add a spanned help child diagnostic that points to the
477 /// parent derived trait.
478 ///
479 /// This is experimental and therefore not exposed outside the crate.
480 #[cfg(feature = "diagnostics")]
481 #[allow(dead_code)]
482 fn emit_with_macro_help_span(self) {
483 use proc_macro::Diagnostic;
484
485 for error in self.flatten() {
486 let needs_help = error.has_span();
487 let diagnostic = error.single_to_diagnostic();
488 Diagnostic::emit(if needs_help {
489 diagnostic.span_help(
490 Span::call_site().unwrap(),
491 "Encountered as part of this derive-mode-macro",
492 )
493 } else {
494 diagnostic
495 })
496 }
497 }
498}
499
500#[cfg(feature = "diagnostics")]
501macro_rules! add_child {
502 ($unspanned:ident, $spanned:ident, $level:ident) => {
503 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
504 #[doc = "# Example"]
505 #[doc = "```rust"]
506 #[doc = "# use darling_core::Error;"]
507 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
508 #[doc = "```"]
509 pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
510 self.children.push(child::ChildDiagnostic::new(
511 child::Level::$level,
512 None,
513 message.to_string(),
514 ));
515 self
516 }
517
518 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
519 #[doc = "# Example"]
520 #[doc = "```rust"]
521 #[doc = "# use darling_core::Error;"]
522 #[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
523 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
524 #[doc = "```"]
525 pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
526 self.children.push(child::ChildDiagnostic::new(
527 child::Level::$level,
528 Some(span.span()),
529 message.to_string(),
530 ));
531 self
532 }
533 };
534}
535
536/// Add child diagnostics to the error.
537///
538/// # Example
539///
540/// ## Code
541///
542/// ```rust
543/// # use darling_core::Error;
544/// # let struct_ident = proc_macro2::Span::call_site();
545/// Error::custom("this is a demo")
546/// .with_span(&struct_ident)
547/// .note("we wrote this")
548/// .help("try doing this instead");
549/// ```
550/// ## Output
551///
552/// ```text
553/// error: this is a demo
554/// --> my_project/my_file.rs:3:5
555/// |
556/// 13 | FooBar { value: String },
557/// | ^^^^^^
558/// |
559/// = note: we wrote this
560/// = help: try doing this instead
561/// ```
562#[cfg(feature = "diagnostics")]
563impl Error {
564 add_child!(error, span_error, Error);
565 add_child!(warning, span_warning, Warning);
566 add_child!(note, span_note, Note);
567 add_child!(help, span_help, Help);
568}
569
570impl StdError for Error {
571 fn description(&self) -> &str {
572 self.kind.description()
573 }
574
575 fn cause(&self) -> Option<&dyn StdError> {
576 None
577 }
578}
579
580impl fmt::Display for Error {
581 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582 write!(f, "{}", self.kind)?;
583 if !self.locations.is_empty() {
584 write!(f, " at {}", self.locations.join("/"))?;
585 }
586
587 Ok(())
588 }
589}
590
591impl From<syn::Error> for Error {
592 fn from(e: syn::Error) -> Self {
593 // This impl assumes there is nothing but the message and span that needs to be preserved
594 // from the passed-in error. If this changes at some point, a new ErrorKind should be made
595 // to hold the syn::Error, and this impl should preserve it unmodified while setting its own
596 // span to be a copy of the passed-in error.
597 Self {
598 span: Some(e.span()),
599 ..Self::custom(msg:e)
600 }
601 }
602}
603
604impl From<Error> for syn::Error {
605 fn from(e: Error) -> Self {
606 if e.len() == 1 {
607 if let Some(span) = e.explicit_span() {
608 // Don't include the location path if the error has an explicit span,
609 // since it will be redundant and isn't consistent with how rustc
610 // exposes errors.
611 syn::Error::new(span, e.kind)
612 } else {
613 // If the error's span is going to be the macro call site, include
614 // the location information to try and help the user pinpoint the issue.
615 syn::Error::new(e.span(), e)
616 }
617 } else {
618 let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
619 let mut error = syn_errors
620 .next()
621 .expect("darling::Error can never be empty");
622
623 for next_error in syn_errors {
624 error.combine(next_error);
625 }
626
627 error
628 }
629 }
630}
631
632// Don't want to publicly commit to Error supporting equality yet, but
633// not having it makes testing very difficult. Note that spans are not
634// considered for equality since that would break testing in most cases.
635#[cfg(test)]
636impl PartialEq for Error {
637 fn eq(&self, other: &Self) -> bool {
638 self.kind == other.kind && self.locations == other.locations
639 }
640}
641
642#[cfg(test)]
643impl Eq for Error {}
644
645impl IntoIterator for Error {
646 type Item = Error;
647 type IntoIter = IntoIter;
648
649 fn into_iter(self) -> IntoIter {
650 if let ErrorKind::Multiple(errors: Vec) = self.kind {
651 IntoIter {
652 inner: IntoIterEnum::Multiple(errors.into_iter()),
653 }
654 } else {
655 IntoIter {
656 inner: IntoIterEnum::Single(iter::once(self)),
657 }
658 }
659 }
660}
661
662enum IntoIterEnum {
663 Single(iter::Once<Error>),
664 Multiple(vec::IntoIter<Error>),
665}
666
667impl Iterator for IntoIterEnum {
668 type Item = Error;
669
670 fn next(&mut self) -> Option<Self::Item> {
671 match *self {
672 IntoIterEnum::Single(ref mut content: impl Iterator) => content.next(),
673 IntoIterEnum::Multiple(ref mut content: &mut IntoIter) => content.next(),
674 }
675 }
676}
677
678/// An iterator that moves out of an `Error`.
679pub struct IntoIter {
680 inner: IntoIterEnum,
681}
682
683impl Iterator for IntoIter {
684 type Item = Error;
685
686 fn next(&mut self) -> Option<Error> {
687 self.inner.next()
688 }
689}
690
691/// Accumulator for errors, for helping call [`Error::multiple`].
692///
693/// See the docs for [`darling::Error`](Error) for more discussion of error handling with darling.
694///
695/// # Panics
696///
697/// `Accumulator` panics on drop unless [`finish`](Self::finish), [`finish_with`](Self::finish_with),
698/// or [`into_inner`](Self::into_inner) has been called, **even if it contains no errors**.
699/// If you want to discard an `Accumulator` that you know to be empty, use `accumulator.finish().unwrap()`.
700///
701/// # Example
702///
703/// ```
704/// # extern crate darling_core as darling;
705/// # struct Thing;
706/// # struct Output;
707/// # impl Thing { fn validate(self) -> darling::Result<Output> { Ok(Output) } }
708/// fn validate_things(inputs: Vec<Thing>) -> darling::Result<Vec<Output>> {
709/// let mut errors = darling::Error::accumulator();
710///
711/// let outputs = inputs
712/// .into_iter()
713/// .filter_map(|thing| errors.handle_in(|| thing.validate()))
714/// .collect::<Vec<_>>();
715///
716/// errors.finish()?;
717/// Ok(outputs)
718/// }
719/// ```
720#[derive(Debug)]
721#[must_use = "Accumulator will panic on drop if not defused."]
722pub struct Accumulator(Option<Vec<Error>>);
723
724impl Accumulator {
725 /// Runs a closure, returning the successful value as `Some`, or collecting the error
726 ///
727 /// The closure's return type is `darling::Result`, so inside it one can use `?`.
728 pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
729 self.handle(f())
730 }
731
732 /// Handles a possible error.
733 ///
734 /// Returns a successful value as `Some`, or collects the error and returns `None`.
735 pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
736 match result {
737 Ok(y) => Some(y),
738 Err(e) => {
739 self.push(e);
740 None
741 }
742 }
743 }
744
745 /// Stop accumulating errors, producing `Ok` if there are no errors or producing
746 /// an error with all those encountered by the accumulator.
747 pub fn finish(self) -> Result<()> {
748 self.finish_with(())
749 }
750
751 /// Bundles the collected errors if there were any, or returns the success value
752 ///
753 /// Call this at the end of your input processing.
754 ///
755 /// If there were no errors recorded, returns `Ok(success)`.
756 /// Otherwise calls [`Error::multiple`] and returns the result as an `Err`.
757 pub fn finish_with<T>(self, success: T) -> Result<T> {
758 let errors = self.into_inner();
759 if errors.is_empty() {
760 Ok(success)
761 } else {
762 Err(Error::multiple(errors))
763 }
764 }
765
766 fn errors(&mut self) -> &mut Vec<Error> {
767 match &mut self.0 {
768 Some(errors) => errors,
769 None => panic!("darling internal error: Accumulator accessed after defuse"),
770 }
771 }
772
773 /// Returns the accumulated errors as a `Vec`.
774 ///
775 /// This function defuses the drop bomb.
776 #[must_use = "Accumulated errors should be handled or propagated to the caller"]
777 pub fn into_inner(mut self) -> Vec<Error> {
778 match self.0.take() {
779 Some(errors) => errors,
780 None => panic!("darling internal error: Accumulator accessed after defuse"),
781 }
782 }
783
784 /// Add one error to the collection.
785 pub fn push(&mut self, error: Error) {
786 self.errors().push(error)
787 }
788
789 /// Finish the current accumulation, and if there are no errors create a new `Self` so processing may continue.
790 ///
791 /// This is shorthand for:
792 ///
793 /// ```rust,ignore
794 /// errors.finish()?;
795 /// errors = Error::accumulator();
796 /// ```
797 ///
798 /// # Drop Behavior
799 /// This function returns a new [`Accumulator`] in the success case.
800 /// This new accumulator is "armed" and will detonate if dropped without being finished.
801 ///
802 /// # Example
803 ///
804 /// ```
805 /// # extern crate darling_core as darling;
806 /// # struct Thing;
807 /// # struct Output;
808 /// # impl Thing { fn validate(&self) -> darling::Result<Output> { Ok(Output) } }
809 /// fn validate(lorem_inputs: &[Thing], ipsum_inputs: &[Thing])
810 /// -> darling::Result<(Vec<Output>, Vec<Output>)> {
811 /// let mut errors = darling::Error::accumulator();
812 ///
813 /// let lorems = lorem_inputs.iter().filter_map(|l| {
814 /// errors.handle(l.validate())
815 /// }).collect();
816 ///
817 /// errors = errors.checkpoint()?;
818 ///
819 /// let ipsums = ipsum_inputs.iter().filter_map(|l| {
820 /// errors.handle(l.validate())
821 /// }).collect();
822 ///
823 /// errors.finish_with((lorems, ipsums))
824 /// }
825 /// # validate(&[], &[]).unwrap();
826 /// ```
827 pub fn checkpoint(self) -> Result<Accumulator> {
828 // The doc comment says on success we "return the Accumulator for future use".
829 // Actually, we have consumed it by feeding it to finish so we make a fresh one.
830 // This is OK since by definition of the success path, it was empty on entry.
831 self.finish()?;
832 Ok(Self::default())
833 }
834}
835
836impl Default for Accumulator {
837 fn default() -> Self {
838 Accumulator(Some(vec![]))
839 }
840}
841
842impl Extend<Error> for Accumulator {
843 fn extend<I>(&mut self, iter: I)
844 where
845 I: IntoIterator<Item = Error>,
846 {
847 self.errors().extend(iter)
848 }
849}
850
851impl Drop for Accumulator {
852 fn drop(&mut self) {
853 // don't try to panic if we are currently unwinding a panic
854 // otherwise we end up with an unhelful "thread panicked while panicking. aborting." message
855 if !std::thread::panicking() {
856 if let Some(errors: &mut Vec) = &mut self.0 {
857 match errors.len() {
858 0 => panic!("darling::error::Accumulator dropped without being finished"),
859 error_count: usize => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
860 }
861 }
862 }
863 }
864}
865
866#[cfg(test)]
867mod tests {
868 use super::Error;
869
870 #[test]
871 fn flatten_noop() {
872 let err = Error::duplicate_field("hello").at("world");
873 assert_eq!(err.clone().flatten(), err);
874 }
875
876 #[test]
877 fn flatten_simple() {
878 let err = Error::multiple(vec![
879 Error::unknown_field("hello").at("world"),
880 Error::missing_field("hell_no").at("world"),
881 ])
882 .at("foo")
883 .flatten();
884
885 assert!(err.location().is_empty());
886
887 let mut err_iter = err.into_iter();
888
889 let first = err_iter.next();
890 assert!(first.is_some());
891 assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
892
893 let second = err_iter.next();
894 assert!(second.is_some());
895
896 assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
897
898 assert!(err_iter.next().is_none());
899 }
900
901 #[test]
902 fn len_single() {
903 let err = Error::duplicate_field("hello");
904 assert_eq!(1, err.len());
905 }
906
907 #[test]
908 fn len_multiple() {
909 let err = Error::multiple(vec![
910 Error::duplicate_field("hello"),
911 Error::missing_field("hell_no"),
912 ]);
913 assert_eq!(2, err.len());
914 }
915
916 #[test]
917 fn len_nested() {
918 let err = Error::multiple(vec![
919 Error::duplicate_field("hello"),
920 Error::multiple(vec![
921 Error::duplicate_field("hi"),
922 Error::missing_field("bye"),
923 Error::multiple(vec![Error::duplicate_field("whatsup")]),
924 ]),
925 ]);
926
927 assert_eq!(4, err.len());
928 }
929
930 #[test]
931 fn accum_ok() {
932 let errs = Error::accumulator();
933 assert_eq!("test", errs.finish_with("test").unwrap());
934 }
935
936 #[test]
937 fn accum_errr() {
938 let mut errs = Error::accumulator();
939 errs.push(Error::custom("foo!"));
940 errs.finish().unwrap_err();
941 }
942
943 #[test]
944 fn accum_into_inner() {
945 let mut errs = Error::accumulator();
946 errs.push(Error::custom("foo!"));
947 let errs: Vec<_> = errs.into_inner();
948 assert_eq!(errs.len(), 1);
949 }
950
951 #[test]
952 #[should_panic(expected = "Accumulator dropped")]
953 fn accum_drop_panic() {
954 let _errs = Error::accumulator();
955 }
956
957 #[test]
958 #[should_panic(expected = "2 errors")]
959 fn accum_drop_panic_with_error_count() {
960 let mut errors = Error::accumulator();
961 errors.push(Error::custom("first"));
962 errors.push(Error::custom("second"));
963 }
964
965 #[test]
966 fn accum_checkpoint_error() {
967 let mut errs = Error::accumulator();
968 errs.push(Error::custom("foo!"));
969 errs.checkpoint().unwrap_err();
970 }
971
972 #[test]
973 #[should_panic(expected = "Accumulator dropped")]
974 fn accum_checkpoint_drop_panic() {
975 let mut errs = Error::accumulator();
976 errs = errs.checkpoint().unwrap();
977 let _ = errs;
978 }
979}
980