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