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::{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 /// Creates a new error for a field which has an unexpected literal type. This will automatically
153 /// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
154 /// literal value.
155 ///
156 /// # Usage
157 /// This is most frequently used in overrides of the `FromMeta::from_value` method.
158 ///
159 /// ```rust
160 /// # // pretend darling_core is darling so the doc example looks correct.
161 /// # extern crate darling_core as darling;
162 /// # extern crate syn;
163 ///
164 /// use darling::{FromMeta, Error, Result};
165 /// use syn::{Lit, LitStr};
166 ///
167 /// pub struct Foo(String);
168 ///
169 /// impl FromMeta for Foo {
170 /// fn from_value(value: &Lit) -> Result<Self> {
171 /// if let Lit::Str(ref lit_str) = *value {
172 /// Ok(Foo(lit_str.value()))
173 /// } else {
174 /// Err(Error::unexpected_lit_type(value))
175 /// }
176 /// }
177 /// }
178 ///
179 /// # fn main() {}
180 /// ```
181 pub fn unexpected_lit_type(lit: &Lit) -> Self {
182 Error::unexpected_type(match *lit {
183 Lit::Str(_) => "string",
184 Lit::ByteStr(_) => "byte string",
185 Lit::Byte(_) => "byte",
186 Lit::Char(_) => "char",
187 Lit::Int(_) => "int",
188 Lit::Float(_) => "float",
189 Lit::Bool(_) => "bool",
190 Lit::Verbatim(_) => "verbatim",
191 })
192 .with_span(lit)
193 }
194
195 /// Creates a new error for a value which doesn't match a set of expected literals.
196 pub fn unknown_value(value: &str) -> Self {
197 Error::new(ErrorKind::UnknownValue(value.into()))
198 }
199
200 /// Creates a new error for a list which did not get enough items to proceed.
201 pub fn too_few_items(min: usize) -> Self {
202 Error::new(ErrorKind::TooFewItems(min))
203 }
204
205 /// Creates a new error when a list got more items than it supports. The `max` argument
206 /// is the largest number of items the receiver could accept.
207 pub fn too_many_items(max: usize) -> Self {
208 Error::new(ErrorKind::TooManyItems(max))
209 }
210
211 /// Bundle a set of multiple errors into a single `Error` instance.
212 ///
213 /// Usually it will be more convenient to use an [`error::Accumulator`](Accumulator).
214 ///
215 /// # Panics
216 /// This function will panic if `errors.is_empty() == true`.
217 pub fn multiple(mut errors: Vec<Error>) -> Self {
218 match errors.len() {
219 1 => errors
220 .pop()
221 .expect("Error array of length 1 has a first item"),
222 0 => panic!("Can't deal with 0 errors"),
223 _ => Error::new(ErrorKind::Multiple(errors)),
224 }
225 }
226
227 /// Creates an error collector, for aggregating multiple errors
228 ///
229 /// See [`Accumulator`] for details.
230 pub fn accumulator() -> Accumulator {
231 Default::default()
232 }
233}
234
235impl Error {
236 /// Create a new error about a literal string that doesn't match a set of known
237 /// or permissible values. This function can be made public if the API proves useful
238 /// beyond impls for `syn` types.
239 pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
240 Error::unknown_value(&value.value()).with_span(node:value)
241 }
242}
243
244/// Error instance methods
245#[allow(clippy::len_without_is_empty)] // Error can never be empty
246impl Error {
247 /// Check if this error is associated with a span in the token stream.
248 pub fn has_span(&self) -> bool {
249 self.span.is_some()
250 }
251
252 /// Tie a span to the error if none is already present. This is used in `darling::FromMeta`
253 /// and other traits to attach errors to the most specific possible location in the input
254 /// source code.
255 ///
256 /// All `darling`-built impls, either from the crate or from the proc macro, will call this
257 /// when appropriate during parsing, so it should not be necessary to call this unless you have
258 /// overridden:
259 ///
260 /// * `FromMeta::from_meta`
261 /// * `FromMeta::from_nested_meta`
262 /// * `FromMeta::from_value`
263 pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
264 if !self.has_span() {
265 self.span = Some(node.span());
266 }
267
268 self
269 }
270
271 /// Get a span for the error.
272 ///
273 /// # Return Value
274 /// This function will return [`Span::call_site()`](proc_macro2::Span) if [`Self::has_span`] is `false`.
275 /// To get the span only if one has been explicitly set for `self`, instead use [`Error::explicit_span`].
276 pub fn span(&self) -> Span {
277 self.span.unwrap_or_else(Span::call_site)
278 }
279
280 /// Get the span for `self`, if one has been set.
281 pub fn explicit_span(&self) -> Option<Span> {
282 self.span
283 }
284
285 /// Recursively converts a tree of errors to a flattened list.
286 ///
287 /// # Child Diagnostics
288 /// If the `diagnostics` feature is enabled, any child diagnostics on `self`
289 /// will be cloned down to all the errors within `self`.
290 pub fn flatten(self) -> Self {
291 Error::multiple(self.into_vec())
292 }
293
294 fn into_vec(self) -> Vec<Self> {
295 if let ErrorKind::Multiple(errors) = self.kind {
296 let locations = self.locations;
297
298 #[cfg(feature = "diagnostics")]
299 let children = self.children;
300
301 errors
302 .into_iter()
303 .flat_map(|error| {
304 // This is mutated if the diagnostics feature is enabled
305 #[allow(unused_mut)]
306 let mut error = error.prepend_at(locations.clone());
307
308 // Any child diagnostics in `self` are cloned down to all the distinct
309 // errors contained in `self`.
310 #[cfg(feature = "diagnostics")]
311 error.children.extend(children.iter().cloned());
312
313 error.into_vec()
314 })
315 .collect()
316 } else {
317 vec![self]
318 }
319 }
320
321 /// Adds a location to the error, such as a field or variant.
322 /// Locations must be added in reverse order of specificity.
323 pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
324 self.locations.insert(0, location.to_string());
325 self
326 }
327
328 /// Adds a location to the error, such as a field or variant.
329 /// Locations must be added in reverse order of specificity. This is a helper function to avoid
330 /// repeating path to string logic.
331 pub fn at_path(self, path: &Path) -> Self {
332 self.at(path_to_string(path))
333 }
334
335 /// Gets the number of individual errors in this error.
336 ///
337 /// This function never returns `0`, as it's impossible to construct
338 /// a multi-error from an empty `Vec`.
339 pub fn len(&self) -> usize {
340 self.kind.len()
341 }
342
343 /// Adds a location chain to the head of the error's existing locations.
344 fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
345 if !locations.is_empty() {
346 locations.extend(self.locations);
347 self.locations = locations;
348 }
349
350 self
351 }
352
353 /// Gets the location slice.
354 #[cfg(test)]
355 pub(crate) fn location(&self) -> Vec<&str> {
356 self.locations.iter().map(|i| i.as_str()).collect()
357 }
358
359 /// Write this error and any children as compile errors into a `TokenStream` to
360 /// be returned by the proc-macro.
361 ///
362 /// The behavior of this method will be slightly different if the `diagnostics` feature
363 /// is enabled: In that case, the diagnostics will be emitted immediately by this call,
364 /// and an empty `TokenStream` will be returned.
365 ///
366 /// Return these tokens unmodified to avoid disturbing the attached span information.
367 ///
368 /// # Usage
369 /// ```rust,ignore
370 /// // in your proc-macro function
371 /// let opts = match MyOptions::from_derive_input(&ast) {
372 /// Ok(val) => val,
373 /// Err(err) => {
374 /// return err.write_errors();
375 /// }
376 /// }
377 /// ```
378 pub fn write_errors(self) -> TokenStream {
379 #[cfg(feature = "diagnostics")]
380 {
381 self.emit();
382 TokenStream::default()
383 }
384
385 #[cfg(not(feature = "diagnostics"))]
386 {
387 syn::Error::from(self).into_compile_error()
388 }
389 }
390
391 #[cfg(feature = "diagnostics")]
392 fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
393 use proc_macro::{Diagnostic, Level};
394
395 // Delegate to dedicated error formatters when applicable.
396 //
397 // If span information is available, don't include the error property path
398 // since it's redundant and not consistent with native compiler diagnostics.
399 let diagnostic = match self.kind {
400 ErrorKind::UnknownField(euf) => euf.into_diagnostic(self.span),
401 _ => match self.span {
402 Some(span) => span.unwrap().error(self.kind.to_string()),
403 None => Diagnostic::new(Level::Error, self.to_string()),
404 },
405 };
406
407 self.children
408 .into_iter()
409 .fold(diagnostic, |out, child| child.append_to(out))
410 }
411
412 /// Transform this error and its children into a list of compiler diagnostics
413 /// and emit them. If the `Error` has associated span information, the diagnostics
414 /// will identify the correct location in source code automatically.
415 ///
416 /// # Stability
417 /// This is only available on `nightly` until the compiler `proc_macro_diagnostic`
418 /// feature stabilizes. Until then, it may break at any time.
419 #[cfg(feature = "diagnostics")]
420 pub fn emit(self) {
421 for error in self.flatten() {
422 error.single_to_diagnostic().emit()
423 }
424 }
425
426 /// Transform the error into a compiler diagnostic and - if the diagnostic points to
427 /// a specific code location - add a spanned help child diagnostic that points to the
428 /// parent derived trait.
429 ///
430 /// This is experimental and therefore not exposed outside the crate.
431 #[cfg(feature = "diagnostics")]
432 #[allow(dead_code)]
433 fn emit_with_macro_help_span(self) {
434 use proc_macro::Diagnostic;
435
436 for error in self.flatten() {
437 let needs_help = error.has_span();
438 let diagnostic = error.single_to_diagnostic();
439 Diagnostic::emit(if needs_help {
440 diagnostic.span_help(
441 Span::call_site().unwrap(),
442 "Encountered as part of this derive-mode-macro",
443 )
444 } else {
445 diagnostic
446 })
447 }
448 }
449}
450
451#[cfg(feature = "diagnostics")]
452macro_rules! add_child {
453 ($unspanned:ident, $spanned:ident, $level:ident) => {
454 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error.")]
455 #[doc = "# Example"]
456 #[doc = "```rust"]
457 #[doc = "# use darling_core::Error;"]
458 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($unspanned), r#"("message content");"#)]
459 #[doc = "```"]
460 pub fn $unspanned<T: fmt::Display>(mut self, message: T) -> Self {
461 self.children.push(child::ChildDiagnostic::new(
462 child::Level::$level,
463 None,
464 message.to_string(),
465 ));
466 self
467 }
468
469 #[doc = concat!("Add a child ", stringify!($unspanned), " message to this error with its own span.")]
470 #[doc = "# Example"]
471 #[doc = "```rust"]
472 #[doc = "# use darling_core::Error;"]
473 #[doc = "# let item_to_span = proc_macro2::Span::call_site();"]
474 #[doc = concat!(r#"Error::custom("Example")."#, stringify!($spanned), r#"(&item_to_span, "message content");"#)]
475 #[doc = "```"]
476 pub fn $spanned<S: Spanned, T: fmt::Display>(mut self, span: &S, message: T) -> Self {
477 self.children.push(child::ChildDiagnostic::new(
478 child::Level::$level,
479 Some(span.span()),
480 message.to_string(),
481 ));
482 self
483 }
484 };
485}
486
487/// Add child diagnostics to the error.
488///
489/// # Example
490///
491/// ## Code
492///
493/// ```rust
494/// # use darling_core::Error;
495/// # let struct_ident = proc_macro2::Span::call_site();
496/// Error::custom("this is a demo")
497/// .with_span(&struct_ident)
498/// .note("we wrote this")
499/// .help("try doing this instead");
500/// ```
501/// ## Output
502///
503/// ```text
504/// error: this is a demo
505/// --> my_project/my_file.rs:3:5
506/// |
507/// 13 | FooBar { value: String },
508/// | ^^^^^^
509/// |
510/// = note: we wrote this
511/// = help: try doing this instead
512/// ```
513#[cfg(feature = "diagnostics")]
514impl Error {
515 add_child!(error, span_error, Error);
516 add_child!(warning, span_warning, Warning);
517 add_child!(note, span_note, Note);
518 add_child!(help, span_help, Help);
519}
520
521impl StdError for Error {
522 fn description(&self) -> &str {
523 self.kind.description()
524 }
525
526 fn cause(&self) -> Option<&dyn StdError> {
527 None
528 }
529}
530
531impl fmt::Display for Error {
532 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
533 write!(f, "{}", self.kind)?;
534 if !self.locations.is_empty() {
535 write!(f, " at {}", self.locations.join("/"))?;
536 }
537
538 Ok(())
539 }
540}
541
542impl From<syn::Error> for Error {
543 fn from(e: syn::Error) -> Self {
544 // This impl assumes there is nothing but the message and span that needs to be preserved
545 // from the passed-in error. If this changes at some point, a new ErrorKind should be made
546 // to hold the syn::Error, and this impl should preserve it unmodified while setting its own
547 // span to be a copy of the passed-in error.
548 Self {
549 span: Some(e.span()),
550 ..Self::custom(msg:e)
551 }
552 }
553}
554
555impl From<Error> for syn::Error {
556 fn from(e: Error) -> Self {
557 if e.len() == 1 {
558 if let Some(span) = e.explicit_span() {
559 // Don't include the location path if the error has an explicit span,
560 // since it will be redundant and isn't consistent with how rustc
561 // exposes errors.
562 syn::Error::new(span, e.kind)
563 } else {
564 // If the error's span is going to be the macro call site, include
565 // the location information to try and help the user pinpoint the issue.
566 syn::Error::new(e.span(), e)
567 }
568 } else {
569 let mut syn_errors = e.flatten().into_iter().map(syn::Error::from);
570 let mut error = syn_errors
571 .next()
572 .expect("darling::Error can never be empty");
573
574 for next_error in syn_errors {
575 error.combine(next_error);
576 }
577
578 error
579 }
580 }
581}
582
583// Don't want to publicly commit to Error supporting equality yet, but
584// not having it makes testing very difficult. Note that spans are not
585// considered for equality since that would break testing in most cases.
586#[cfg(test)]
587impl PartialEq for Error {
588 fn eq(&self, other: &Self) -> bool {
589 self.kind == other.kind && self.locations == other.locations
590 }
591}
592
593#[cfg(test)]
594impl Eq for Error {}
595
596impl IntoIterator for Error {
597 type Item = Error;
598 type IntoIter = IntoIter;
599
600 fn into_iter(self) -> IntoIter {
601 if let ErrorKind::Multiple(errors: Vec) = self.kind {
602 IntoIter {
603 inner: IntoIterEnum::Multiple(errors.into_iter()),
604 }
605 } else {
606 IntoIter {
607 inner: IntoIterEnum::Single(iter::once(self)),
608 }
609 }
610 }
611}
612
613enum IntoIterEnum {
614 Single(iter::Once<Error>),
615 Multiple(vec::IntoIter<Error>),
616}
617
618impl Iterator for IntoIterEnum {
619 type Item = Error;
620
621 fn next(&mut self) -> Option<Self::Item> {
622 match *self {
623 IntoIterEnum::Single(ref mut content: impl Iterator) => content.next(),
624 IntoIterEnum::Multiple(ref mut content: &mut IntoIter) => content.next(),
625 }
626 }
627}
628
629/// An iterator that moves out of an `Error`.
630pub struct IntoIter {
631 inner: IntoIterEnum,
632}
633
634impl Iterator for IntoIter {
635 type Item = Error;
636
637 fn next(&mut self) -> Option<Error> {
638 self.inner.next()
639 }
640}
641
642/// Accumulator for errors, for helping call [`Error::multiple`].
643///
644/// See the docs for [`darling::Error`](Error) for more discussion of error handling with darling.
645///
646/// # Panics
647///
648/// `Accumulator` panics on drop unless [`finish`](Self::finish), [`finish_with`](Self::finish_with),
649/// or [`into_inner`](Self::into_inner) has been called, **even if it contains no errors**.
650/// If you want to discard an `Accumulator` that you know to be empty, use `accumulator.finish().unwrap()`.
651///
652/// # Example
653///
654/// ```
655/// # extern crate darling_core as darling;
656/// # struct Thing;
657/// # struct Output;
658/// # impl Thing { fn validate(self) -> darling::Result<Output> { Ok(Output) } }
659/// fn validate_things(inputs: Vec<Thing>) -> darling::Result<Vec<Output>> {
660/// let mut errors = darling::Error::accumulator();
661///
662/// let outputs = inputs
663/// .into_iter()
664/// .filter_map(|thing| errors.handle_in(|| thing.validate()))
665/// .collect::<Vec<_>>();
666///
667/// errors.finish()?;
668/// Ok(outputs)
669/// }
670/// ```
671#[derive(Debug)]
672#[must_use = "Accumulator will panic on drop if not defused."]
673pub struct Accumulator(Option<Vec<Error>>);
674
675impl Accumulator {
676 /// Runs a closure, returning the successful value as `Some`, or collecting the error
677 ///
678 /// The closure's return type is `darling::Result`, so inside it one can use `?`.
679 pub fn handle_in<T, F: FnOnce() -> Result<T>>(&mut self, f: F) -> Option<T> {
680 self.handle(f())
681 }
682
683 /// Handles a possible error.
684 ///
685 /// Returns a successful value as `Some`, or collects the error and returns `None`.
686 pub fn handle<T>(&mut self, result: Result<T>) -> Option<T> {
687 match result {
688 Ok(y) => Some(y),
689 Err(e) => {
690 self.push(e);
691 None
692 }
693 }
694 }
695
696 /// Stop accumulating errors, producing `Ok` if there are no errors or producing
697 /// an error with all those encountered by the accumulator.
698 pub fn finish(self) -> Result<()> {
699 self.finish_with(())
700 }
701
702 /// Bundles the collected errors if there were any, or returns the success value
703 ///
704 /// Call this at the end of your input processing.
705 ///
706 /// If there were no errors recorded, returns `Ok(success)`.
707 /// Otherwise calls [`Error::multiple`] and returns the result as an `Err`.
708 pub fn finish_with<T>(self, success: T) -> Result<T> {
709 let errors = self.into_inner();
710 if errors.is_empty() {
711 Ok(success)
712 } else {
713 Err(Error::multiple(errors))
714 }
715 }
716
717 fn errors(&mut self) -> &mut Vec<Error> {
718 match &mut self.0 {
719 Some(errors) => errors,
720 None => panic!("darling internal error: Accumulator accessed after defuse"),
721 }
722 }
723
724 /// Returns the accumulated errors as a `Vec`.
725 ///
726 /// This function defuses the drop bomb.
727 #[must_use = "Accumulated errors should be handled or propagated to the caller"]
728 pub fn into_inner(mut self) -> Vec<Error> {
729 match std::mem::replace(&mut self.0, None) {
730 Some(errors) => errors,
731 None => panic!("darling internal error: Accumulator accessed after defuse"),
732 }
733 }
734
735 /// Add one error to the collection.
736 pub fn push(&mut self, error: Error) {
737 self.errors().push(error)
738 }
739
740 /// Finish the current accumulation, and if there are no errors create a new `Self` so processing may continue.
741 ///
742 /// This is shorthand for:
743 ///
744 /// ```rust,ignore
745 /// errors.finish()?;
746 /// errors = Error::accumulator();
747 /// ```
748 ///
749 /// # Drop Behavior
750 /// This function returns a new [`Accumulator`] in the success case.
751 /// This new accumulator is "armed" and will detonate if dropped without being finished.
752 ///
753 /// # Example
754 ///
755 /// ```
756 /// # extern crate darling_core as darling;
757 /// # struct Thing;
758 /// # struct Output;
759 /// # impl Thing { fn validate(&self) -> darling::Result<Output> { Ok(Output) } }
760 /// fn validate(lorem_inputs: &[Thing], ipsum_inputs: &[Thing])
761 /// -> darling::Result<(Vec<Output>, Vec<Output>)> {
762 /// let mut errors = darling::Error::accumulator();
763 ///
764 /// let lorems = lorem_inputs.iter().filter_map(|l| {
765 /// errors.handle(l.validate())
766 /// }).collect();
767 ///
768 /// errors = errors.checkpoint()?;
769 ///
770 /// let ipsums = ipsum_inputs.iter().filter_map(|l| {
771 /// errors.handle(l.validate())
772 /// }).collect();
773 ///
774 /// errors.finish_with((lorems, ipsums))
775 /// }
776 /// # validate(&[], &[]).unwrap();
777 /// ```
778 pub fn checkpoint(self) -> Result<Accumulator> {
779 // The doc comment says on success we "return the Accumulator for future use".
780 // Actually, we have consumed it by feeding it to finish so we make a fresh one.
781 // This is OK since by definition of the success path, it was empty on entry.
782 self.finish()?;
783 Ok(Self::default())
784 }
785}
786
787impl Default for Accumulator {
788 fn default() -> Self {
789 Accumulator(Some(vec![]))
790 }
791}
792
793impl Extend<Error> for Accumulator {
794 fn extend<I>(&mut self, iter: I)
795 where
796 I: IntoIterator<Item = Error>,
797 {
798 self.errors().extend(iter)
799 }
800}
801
802impl Drop for Accumulator {
803 fn drop(&mut self) {
804 // don't try to panic if we are currently unwinding a panic
805 // otherwise we end up with an unhelful "thread panicked while panicking. aborting." message
806 if !std::thread::panicking() {
807 if let Some(errors: &mut Vec) = &mut self.0 {
808 match errors.len() {
809 0 => panic!("darling::error::Accumulator dropped without being finished"),
810 error_count: usize => panic!("darling::error::Accumulator dropped without being finished. {} errors were lost.", error_count)
811 }
812 }
813 }
814 }
815}
816
817#[cfg(test)]
818mod tests {
819 use super::Error;
820
821 #[test]
822 fn flatten_noop() {
823 let err = Error::duplicate_field("hello").at("world");
824 assert_eq!(err.clone().flatten(), err);
825 }
826
827 #[test]
828 fn flatten_simple() {
829 let err = Error::multiple(vec![
830 Error::unknown_field("hello").at("world"),
831 Error::missing_field("hell_no").at("world"),
832 ])
833 .at("foo")
834 .flatten();
835
836 assert!(err.location().is_empty());
837
838 let mut err_iter = err.into_iter();
839
840 let first = err_iter.next();
841 assert!(first.is_some());
842 assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
843
844 let second = err_iter.next();
845 assert!(second.is_some());
846
847 assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
848
849 assert!(err_iter.next().is_none());
850 }
851
852 #[test]
853 fn len_single() {
854 let err = Error::duplicate_field("hello");
855 assert_eq!(1, err.len());
856 }
857
858 #[test]
859 fn len_multiple() {
860 let err = Error::multiple(vec![
861 Error::duplicate_field("hello"),
862 Error::missing_field("hell_no"),
863 ]);
864 assert_eq!(2, err.len());
865 }
866
867 #[test]
868 fn len_nested() {
869 let err = Error::multiple(vec![
870 Error::duplicate_field("hello"),
871 Error::multiple(vec![
872 Error::duplicate_field("hi"),
873 Error::missing_field("bye"),
874 Error::multiple(vec![Error::duplicate_field("whatsup")]),
875 ]),
876 ]);
877
878 assert_eq!(4, err.len());
879 }
880
881 #[test]
882 fn accum_ok() {
883 let errs = Error::accumulator();
884 assert_eq!("test", errs.finish_with("test").unwrap());
885 }
886
887 #[test]
888 fn accum_errr() {
889 let mut errs = Error::accumulator();
890 errs.push(Error::custom("foo!"));
891 errs.finish().unwrap_err();
892 }
893
894 #[test]
895 fn accum_into_inner() {
896 let mut errs = Error::accumulator();
897 errs.push(Error::custom("foo!"));
898 let errs: Vec<_> = errs.into_inner();
899 assert_eq!(errs.len(), 1);
900 }
901
902 #[test]
903 #[should_panic(expected = "Accumulator dropped")]
904 fn accum_drop_panic() {
905 let _errs = Error::accumulator();
906 }
907
908 #[test]
909 #[should_panic(expected = "2 errors")]
910 fn accum_drop_panic_with_error_count() {
911 let mut errors = Error::accumulator();
912 errors.push(Error::custom("first"));
913 errors.push(Error::custom("second"));
914 }
915
916 #[test]
917 fn accum_checkpoint_error() {
918 let mut errs = Error::accumulator();
919 errs.push(Error::custom("foo!"));
920 errs.checkpoint().unwrap_err();
921 }
922
923 #[test]
924 #[should_panic(expected = "Accumulator dropped")]
925 fn accum_checkpoint_drop_panic() {
926 let mut errs = Error::accumulator();
927 errs = errs.checkpoint().unwrap();
928 let _ = errs;
929 }
930}
931