1#![cfg_attr(not(feature = "std"), no_std)]
2#![cfg_attr(feature = "unstable", feature(allocator_api, try_reserve))]
3
4extern crate alloc;
5
6#[cfg(not(feature = "std"))]
165mod error;
166
167#[cfg(feature = "std")]
169pub use std::error::Error;
170
171#[cfg(not(feature = "std"))]
173pub use error::Error;
174
175#[macro_export]
176macro_rules! custom_error {
177 (
178 $( #[$meta_attribute:meta] )* $visibility:vis $errtype:ident $( < $(
182 $type_param:tt ),*
184 > )?
185 $(
186 $( #[$field_meta:meta] )* $field:ident $( { $(
189 $( #[$attr_meta:meta] )* $attr_name:ident :
192 $attr_type:ty ),* } )?
194 =
195 $( @{ $($msg_fun:tt)* } )?
196 $($msg:expr)? ),*
198 $(,)* ) => {
200 $( #[$meta_attribute] )*
201 #[derive(Debug)]
202 $visibility enum $errtype $( < $($type_param),* > )* {
203 $(
204 $( #[$field_meta] )*
205 $field
206 $( { $( $( #[$attr_meta] )* $attr_name : $attr_type ),* } )*
207 ),*
208 }
209
210 $crate::add_type_bounds! {
211 ( $($($type_param),*)* )
212 (core::fmt::Debug + core::fmt::Display)
213 { impl <} {> $crate::Error
214 for $errtype $( < $($type_param),* > )*
215 {
216 fn source(&self) -> Option<&(dyn $crate::Error + 'static)>
217 {
218 #[allow(unused_variables, unreachable_code)]
219 match self {$(
220 $errtype::$field $( { $( $attr_name ),* } )* => {
221 $( $(
222 $crate::return_if_source!($attr_name, $attr_name)
223 );* ;)*
224 None
225 }
226 ),*}
227 }
228 }
229 }}
230
231 $crate::impl_error_conversion!{
232 ( $errtype $(< $($type_param),* >)* )
233 $([
234 $field,
235 $($(
236 $attr_name,
237 $attr_name,
238 $attr_type
239 ),*),*
240 ])*
241 }
242
243 $crate::add_type_bounds! {
244 ( $($($type_param),*)* )
245 (alloc::string::ToString)
246 { impl <} {> core::fmt::Display
247 for $errtype $( < $($type_param),* > )*
248 {
249 fn fmt(&self, formatter: &mut core::fmt::Formatter)
250 -> core::fmt::Result
251 {
252 match self {$(
253 $errtype::$field $( { $( $attr_name ),* } )* => {
254 $(write!(formatter, "{}", ($($msg_fun)*) )?;)*
255 $crate::display_message!(formatter, $($($attr_name),*),* | $($msg)*);
256 Ok(())
257 }
258 ),*}
259 }
260 }
261 }}
262 };
263
264 (
266 $( #[$meta_attribute:meta] )* $visibility:vis $errtype:ident $( < $(
270 $type_param:tt ),*
272 > )?
273 { $(
274 $( #[$field_meta:meta] )* $field_name:ident :
277 $field_type:ty ),* }
279 =
280 $( @{ $($msg_fun:tt)* } )?
281 $($msg:expr)? $(,)* ) => {
284 $( #[$meta_attribute] )*
285 #[derive(Debug)]
286 $visibility struct $errtype $( < $($type_param),* > )* {
287 $(
288 $( #[$field_meta] )*
289 pub $field_name : $field_type
290 ),*
291 }
292
293 $crate::add_type_bounds! {
294 ( $($($type_param),*)* )
295 (core::fmt::Debug + core::fmt::Display)
296 { impl <} {> $crate::Error
297 for $errtype $( < $($type_param),* > )*
298 {
299 #[allow(unused_variables, unreachable_code)]
300 fn source(&self) -> Option<&(dyn $crate::Error + 'static)>
301 {
302 #[allow(unused_variables, unreachable_code)]
303 match self {
304 $errtype { $( $field_name ),* } => {
305 $({
306 $crate::return_if_source!($field_name, $field_name)
307 });*
308 None
309 }
310 }
311 }
312 }
313 }}
314
315 $crate::impl_error_conversion_for_struct!{
316 $errtype $(< $($type_param),* >)*,
317 $( $field_name: $field_type ),*
318 }
319
320 $crate::add_type_bounds! {
321 ( $($($type_param),*)* )
322 (alloc::string::ToString)
323 { impl <} {> core::fmt::Display
324 for $errtype $( < $($type_param),* > )*
325 {
326 fn fmt(&self, formatter: &mut core::fmt::Formatter)
327 -> core::fmt::Result
328 {
329 $(
332 let $field_name = &self.$field_name;
333 )*
334 $(write!(formatter, "{}", ($($msg_fun)*) )?;)*
335 $crate::display_message!(formatter, $($field_name),* | $($msg)*);
336 Ok(())
337 }
338 }
339 }}
340 };
341}
342
343#[doc(hidden)]
344#[macro_export]
345macro_rules! return_if_source {
346 (source, $attr_name:ident) => {{
348 return Some(core::borrow::Borrow::borrow($attr_name));
351 }};
352 ($_attr_name:ident, $_repeat:ident ) => {
354 ()
355 };
356}
357
358#[doc(hidden)]
359#[macro_export]
360macro_rules! impl_error_conversion {
361 ( ( $($prefix:tt)* ) [ $($field_data:tt)* ] $($rest:tt)* ) => {
362 $crate::impl_error_conversion!{$($prefix)*, $($field_data)*}
363 $crate::impl_error_conversion!{ ($($prefix)*) $($rest)*}
364 };
365 ( ( $($prefix:tt)* ) ) => {};
366 (
368 $errtype:ident $( < $($type_param:tt),* > )*,
369 $field:ident,
370 source,
371 $source:ident,
372 $source_type:ty
373 ) => {
374 impl $( < $($type_param),* > )* From<$source_type>
375 for $errtype $( < $($type_param),* > )* {
376 fn from(source: $source_type) -> Self {
377 $errtype::$field { source }
378 }
379 }
380 };
381 (
382 $_errtype:ident $( < $($_errtype_type_param:tt),* > )*,
383 $_field:ident,
384 $(
385 $_:ident,
386 $_repeated:ident,
387 $_type:ty
388 ),*
389 ) => {}; }
391
392#[doc(hidden)]
393#[macro_export]
394macro_rules! impl_error_conversion_for_struct {
395 ( ( $($prefix:tt)* ) ) => {};
396 (
398 $errtype:ident, $( < $($type_param:tt),* > )*
399 source: $source_type:ty
400 ) => {
401 impl $( < $($type_param),* > )* From<$source_type>
402 for $errtype $( < $($type_param),* > )* {
403 fn from(source: $source_type) -> Self { $errtype { source } }
404 }
405 };
406 (
407 $_errtype:ident $( < $($_errtype_type_param:tt),* > )*,
408 $( $_field:ident: $_type:ty ),*
409 ) => {}; }
411
412#[doc(hidden)]
413#[macro_export]
414macro_rules! display_message {
415 ($formatter:expr, $($attr:ident),* | $msg:expr) => {
416 write!(
417 $formatter,
418 concat!($msg $(, "{", stringify!($attr), ":.0}" )*)
419 $( , $attr = $attr.to_string() )*
420 )?;
421 };
422 ($formatter:expr, $($attr:ident),* | ) => {};
423}
424
425#[doc(hidden)]
429#[macro_export]
430macro_rules! add_type_bounds {
431 (
432 ( $typ:ident $(, $rest:tt)* ) ( $($bounds:tt)* )
434 { $($prefix:tt)* }
435 { $($suffix:tt)* }
436 ) => {
437 add_type_bounds!{
438 ( $(, $rest)* )
439 ( $($bounds)* )
440 { $($prefix)* $typ : $($bounds)*}
441 { $($suffix)* }
442 }
443 };
444 (
445 ( $lifetime:tt $(, $rest:tt)* ) ( $($bounds:tt)* )
447 { $($prefix:tt)* }
448 { $($suffix:tt)* }
449 ) => {
450 add_type_bounds!{
451 ( $(, $rest)* )
452 ( $($bounds)* )
453 { $($prefix)* $lifetime }
454 { $($suffix)* }
455 }
456 };
457 (
458 ( , $($rest:tt)* ) ( $($bounds:tt)* )
460 { $($prefix:tt)* }
461 { $($suffix:tt)* }
462 ) => {
463 add_type_bounds!{
464 ( $($rest)* )
465 ( $($bounds)* )
466 { $($prefix)* , }
467 { $($suffix)* }
468 }
469 };
470 (
471 ( ) ( $($bounds:tt)* )
473 { $($prefix:tt)* }
474 { $($suffix:tt)* }
475 ) => {
476 $($prefix)* $($suffix)*
477 }
478}
479
480#[cfg(all(test, feature = "std"))]
481mod tests {
482 use std::error::Error;
483 use std::str::FromStr;
484
485 #[test]
486 fn single_error_case() {
487 custom_error!(MyError Bad="bad");
488 assert_eq!("bad", MyError::Bad.to_string());
489 }
490
491 #[test]
492 fn single_error_struct_case() {
493 custom_error!(MyError {} = "bad");
494 assert_eq!("bad", MyError {}.to_string());
495 }
496
497 #[test]
498 #[allow(dead_code)]
499 fn three_error_cases() {
500 custom_error!(MyError NotPerfect=":/", Bad="bad", Awfull="arghhh");
501 assert_eq!("arghhh", MyError::Awfull.to_string())
502 }
503
504 #[test]
505 fn with_error_data() {
506 custom_error!(MyError
507 Bad = "bad",
508 Catastrophic{broken_things:u8} = "{broken_things} things are broken"
509 );
510 assert_eq!("bad", MyError::Bad.to_string());
511 assert_eq!(
512 "9 things are broken",
513 MyError::Catastrophic { broken_things: 9 }.to_string()
514 );
515 }
516
517 #[test]
518 fn with_doc_comments_for_variants() {
519 custom_error! {MyError
520 Bad="bad",
522 Terrible="terrible"
524 }
525 assert!(MyError::Bad.source().is_none());
526 assert!(MyError::Terrible.source().is_none());
527 }
528
529 #[test]
530 #[allow(dead_code)]
531 fn with_doc_comments_for_fields() {
532 custom_error! {MyError
533 Bad {
534 bad: &'static str
536 } = "bad {bad}",
537 Terrible {
538 terrible: &'static str
540 } = "bad {terrible}",
541 }
542 assert!(MyError::Bad { bad: "heh" }.source().is_none());
543 }
544
545 #[test]
546 fn enum_with_field_lifetime() {
547 custom_error!(MyError
548 Problem{description: &'static str} = "{description}"
549 );
550 assert_eq!("bad", MyError::Problem { description: "bad" }.to_string());
551 }
552
553 #[test]
554 fn struct_with_field_lifetime() {
555 custom_error!(MyError {description: &'static str} = "{}");
556 assert_eq!("bad", MyError { description: "bad" }.to_string());
557 }
558
559 #[test]
560 fn enum_with_derive() {
561 custom_error! {
562 #[derive(PartialEq, PartialOrd)]
563 #[derive(Clone)]
564 MyError A = "A", B{b:u8} = "B({b})", C = "C"
565 };
566 assert_eq!(MyError::A, MyError::A);
568 assert_eq!(MyError::B { b: 1 }, MyError::B { b: 1 });
569 assert_ne!(MyError::B { b: 0 }, MyError::B { b: 1 });
570 assert_ne!(MyError::A, MyError::B { b: 1 });
571 assert!(MyError::A < MyError::B { b: 1 });
573 assert!(MyError::B { b: 1 } < MyError::B { b: 2 });
574 assert!(MyError::B { b: 2 } < MyError::C);
575 assert_eq!(MyError::A.clone(), MyError::A);
577 }
578
579 #[test]
580 fn enum_with_pub_derive_and_doc_comment() {
581 custom_error! {
582 #[derive(PartialEq, PartialOrd)]
584 pub MyError A = "A"
585 };
586 assert_eq!(MyError::A, MyError::A);
587 }
588
589 #[test]
590 fn enum_with_box_dyn_source() {
591 custom_error! {pub MyError
593 Dynamic { source: Box<dyn Error> } = "dynamic",
594 }
595 let err = u8::from_str("x").unwrap_err();
596 assert_eq!(
597 err.to_string(),
598 MyError::Dynamic {
599 source: Box::new(err)
600 }
601 .source()
602 .unwrap()
603 .to_string()
604 );
605 }
606
607 #[test]
608 fn struct_with_box_dyn_source() {
609 custom_error! {pub MyError
610 { source: Box<dyn Error> } = "dynamic",
611 }
612 let err = u8::from_str("x").unwrap_err();
613 assert_eq!(
614 err.to_string(),
615 MyError {
616 source: Box::new(err)
617 }
618 .source()
619 .unwrap()
620 .to_string()
621 );
622 }
623
624 #[test]
625 fn struct_with_public_field() {
626 mod inner {
627 custom_error! {pub MyError {x: &'static str} = "{}"}
628 }
629 assert_eq!("hello", inner::MyError { x: "hello" }.to_string());
630 }
631
632 #[test]
633 fn struct_with_field_documentation() {
634 custom_error! {
635 pub MyError {
636 x: &'static str
638 } = "{}"
639 }
640 assert_eq!("hello", MyError { x: "hello" }.to_string());
641 }
642
643 #[test]
644 fn struct_with_error_data() {
645 custom_error!(MyError { broken_things: u8 } = "{broken_things} things are broken");
646 assert_eq!(
647 "9 things are broken",
648 MyError { broken_things: 9 }.to_string()
649 );
650 }
651
652 #[test]
653 fn struct_with_derive() {
654 custom_error!(
655 #[derive(PartialEq, PartialOrd, Clone, Default)]
656 MyError { x: u8 } = ":("
657 );
658 assert_eq!(MyError { x: 9 }, MyError { x: 9 }); assert_eq!(MyError { x: 0 }.clone(), MyError { x: 0 }); assert_eq!(MyError::default(), MyError { x: 0 }); assert!(MyError { x: 0 } < MyError { x: 1 }); }
663
664 #[test]
665 fn with_multiple_error_data() {
666 custom_error!(E X{a:u8, b:u8, c:u8} = "{c} {b} {a}");
667
668 assert_eq!("3 2 1", E::X { a: 1, b: 2, c: 3 }.to_string());
669 }
670
671 #[test]
672 fn struct_with_multiple_error_data() {
673 custom_error!(
674 E {
675 a: u8,
676 b: u8,
677 c: u8
678 } = "{c} {b} {a}"
679 );
680
681 assert_eq!("3 2 1", E { a: 1, b: 2, c: 3 }.to_string());
682 }
683
684 #[test]
685 fn source() {
686 use std::{error::Error, io};
687 custom_error!(E A{source: io::Error}="");
688 let source: io::Error = io::ErrorKind::InvalidData.into();
689 assert_eq!(
690 source.to_string(),
691 E::A { source }.source().unwrap().to_string()
692 );
693 }
694
695 #[test]
696 fn struct_source() {
697 use std::{error::Error, io};
698 custom_error!(E { source: io::Error } = "");
699 let source: io::Error = io::ErrorKind::InvalidData.into();
700 assert_eq!(
701 source.to_string(),
702 E { source }.source().unwrap().to_string()
703 );
704 }
705
706 #[test]
707 fn from_source() {
708 use std::io;
709 custom_error!(E A{source: io::Error}="bella vita");
710 let source = io::Error::from(io::ErrorKind::InvalidData);
711 assert_eq!("bella vita", E::from(source).to_string());
712 }
713
714 #[test]
715 fn struct_from_source() {
716 use std::io;
717 custom_error!(E { source: io::Error } = "bella vita");
718 let source = io::Error::from(io::ErrorKind::InvalidData);
719 assert_eq!("bella vita", E::from(source).to_string());
720 }
721
722 #[test]
723 #[allow(dead_code)]
724 fn with_source_and_others() {
725 use std::{error::Error, io};
726 custom_error!(MyError Zero="", One{x:u8}="", Two{x:u8, source:io::Error}="{x}");
727 fn source() -> io::Error {
728 io::ErrorKind::AlreadyExists.into()
729 }
730 let my_err = MyError::Two {
731 x: 42,
732 source: source(),
733 };
734 assert_eq!("42", my_err.to_string());
735 assert_eq!(source().to_string(), my_err.source().unwrap().to_string());
736 }
737
738 #[test]
739 #[allow(dead_code)]
740 fn struct_with_source_and_others() {
741 use std::{error::Error, io};
742 custom_error!(
743 MyError {
744 x: u8,
745 source: io::Error
746 } = "{x}"
747 );
748 fn source() -> io::Error {
749 io::ErrorKind::AlreadyExists.into()
750 }
751 let my_err = MyError {
752 x: 42,
753 source: source(),
754 };
755 assert_eq!("42", my_err.to_string());
756 assert_eq!(source().to_string(), my_err.source().unwrap().to_string());
757 }
758
759 #[test]
760 fn pub_error() {
761 mod my_mod {
762 custom_error! {pub MyError Case1="case1"}
763 }
764 assert_eq!("case1", my_mod::MyError::Case1.to_string());
765 }
766
767 #[test]
768 fn pub_error_struct() {
769 mod my_mod {
770 custom_error! {pub MyError{} = "case1"}
771 }
772 assert_eq!("case1", my_mod::MyError {}.to_string());
773 }
774
775 #[test]
776 fn pub_error_struct_fields() {
777 mod my_mod {
778 custom_error! {pub MyError{x:u8} = "x={x}"}
779 }
780 assert_eq!("x=9", my_mod::MyError { x: 9 }.to_string());
781 }
782
783 #[test]
784 fn generic_error() {
785 custom_error! {MyError<X,Y> E1{x:X,y:Y}="x={x} y={y}", E2="e2"}
786 assert_eq!("x=42 y=42", MyError::E1 { x: 42u8, y: 42u8 }.to_string());
787 assert_eq!("e2", MyError::E2::<u8, u8>.to_string());
788 }
789
790 #[test]
791 fn generic_error_struct() {
792 custom_error! {MyError<X,Y>{x:X,y:Y}="x={x} y={y}"}
793 assert_eq!("x=42 y=42", MyError { x: 42u8, y: 42u8 }.to_string());
794 }
795
796 #[test]
797 fn single_error_case_with_braces() {
798 custom_error! {MyError Bad="bad"}
799 assert_eq!("bad", MyError::Bad.to_string());
800 }
801
802 #[test]
803 fn single_error_struct_case_with_braces() {
804 custom_error! {MyError{} ="bad"}
805 assert_eq!("bad", MyError {}.to_string())
806 }
807
808 #[test]
809 fn trailing_comma() {
810 custom_error! {MyError1 A="a",}
811 custom_error! {MyError2 A="a", B="b",}
812
813 assert_eq!("a", MyError1::A.to_string());
814 assert_eq!("a", MyError2::A.to_string());
815 assert_eq!("b", MyError2::B.to_string());
816 }
817
818 #[test]
819 fn with_custom_formatting() {
820 custom_error! {MyError
821 Complex{a:u8, b:u8} = @{
822 if a+b == 0 {
823 "zero".to_string()
824 } else {
825 (a+b).to_string()
826 }
827 },
828 Simple = "simple"
829 }
830
831 assert_eq!("zero", MyError::Complex { a: 0, b: 0 }.to_string());
832 assert_eq!("3", MyError::Complex { a: 2, b: 1 }.to_string());
833 assert_eq!("simple", MyError::Simple.to_string());
834 }
835
836 #[test]
837 fn struct_with_custom_formatting() {
838 custom_error! {MyError{a:u8, b:u8} = @{
839 if a+b == 0 {
840 "zero".to_string()
841 } else {
842 (a+b).to_string()
843 }
844 }
845 }
846
847 assert_eq!("zero", MyError { a: 0, b: 0 }.to_string());
848 assert_eq!("3", MyError { a: 2, b: 1 }.to_string());
849 }
850
851 #[test]
852 fn custom_format_source() {
853 use std::io;
854
855 custom_error! {MyError
856 Io{source:io::Error} = @{format!("IO Error occurred: {:?}", source.kind())}
857 }
858
859 assert_eq!(
860 "IO Error occurred: Interrupted",
861 MyError::Io {
862 source: io::ErrorKind::Interrupted.into()
863 }
864 .to_string()
865 )
866 }
867
868 #[test]
869 fn struct_custom_format_source() {
870 use std::io;
871
872 custom_error! {MyError{source:io::Error} = @{format!("IO Error occurred: {:?}", source.kind())} }
873
874 assert_eq!(
875 "IO Error occurred: Interrupted",
876 MyError {
877 source: io::ErrorKind::Interrupted.into()
878 }
879 .to_string()
880 )
881 }
882
883 #[test]
884 fn lifetime_source_param() {
885 #[derive(Debug)]
886 struct SourceError<'my_lifetime> {
887 x: &'my_lifetime str,
888 }
889 impl<'a> std::fmt::Display for SourceError<'a> {
890 fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
891 Ok(())
892 }
893 }
894 impl<'a> std::error::Error for SourceError<'a> {}
895
896 custom_error! { MyError<'source_lifetime>
897 Sourced { lifetimed : SourceError<'source_lifetime> } = @{ lifetimed.x },
898 Other { source: std::fmt::Error } = "other error"
899 }
900
901 let sourced = MyError::Sourced {
902 lifetimed: SourceError {
903 x: "I am the source",
904 },
905 };
906 assert_eq!("I am the source", sourced.to_string());
907 let other_err: MyError = std::fmt::Error.into();
908 assert_eq!("other error", other_err.to_string());
909 }
910
911 #[test]
912 fn struct_lifetime_source_param() {
913 #[derive(Debug)]
914 struct SourceError<'my_lifetime> {
915 x: &'my_lifetime str,
916 }
917 impl<'a> std::fmt::Display for SourceError<'a> {
918 fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result {
919 Ok(())
920 }
921 }
922 impl<'a> std::error::Error for SourceError<'a> {}
923
924 custom_error! { MyError<'source_lifetime>{
925 lifetimed : SourceError<'source_lifetime>
926 } = @{ lifetimed.x },}
927
928 let sourced = MyError {
929 lifetimed: SourceError {
930 x: "I am the source",
931 },
932 };
933 assert_eq!("I am the source", sourced.to_string());
934 }
935
936 #[test]
937 fn lifetime_param_and_type_param() {
938 #[derive(Debug)]
939 struct MyType<'a, T> {
940 data: &'a str,
941 _y: T,
942 }
943 custom_error! { MyError<'a,T>
944 X { d: MyType<'a,T> } = @{ format!("error x: {}", d.data) },
945 Y { d: T } = "error y"
946 }
947 let err = MyError::X {
948 d: MyType {
949 data: "hello",
950 _y: 42i8,
951 },
952 };
953 assert_eq!("error x: hello", err.to_string());
954 let err_y = MyError::Y {
955 d: String::from("my string"),
956 };
957 assert_eq!("error y", err_y.to_string());
958 }
959
960 #[test]
961 fn struct_lifetime_param_and_type_param() {
962 #[derive(Debug)]
963 struct MyType<'a, T> {
964 data: &'a str,
965 _y: T,
966 }
967 custom_error! { MyError<'a,T> {
968 d: MyType<'a,T>
969 } = @{ format!("error x: {}", d.data) } }
970 let err = MyError {
971 d: MyType {
972 data: "hello",
973 _y: 42i8,
974 },
975 };
976 assert_eq!("error x: hello", err.to_string());
977 }
978}