use kaptan_err_propagate::{ErrorId, ErrorReport, KaptanError}; #[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)] enum TestError { #[kaptan_error(id = 65001)] Failed, } #[derive(Clone, Copy, Debug, Eq, PartialEq, KaptanError)] enum OuterError { #[kaptan_error(id = 65002)] Converted(TestError), } impl From for OuterError { fn from(error: TestError) -> Self { Self::Converted(error) } } fn fail() -> Result<(), TestError> { Err(TestError::Failed) } fn middle() -> Result<(), TestError> { fail() } #[kaptan_err_propagate::propagate] fn succeeds() -> Result { Ok(7) } #[kaptan_err_propagate::propagate] fn annotated_outer() -> Result<(), TestError> { middle()?; Ok(()) } #[kaptan_err_propagate::propagate] fn annotated_inner() -> Result<(), TestError> { fail() } fn unannotated_outer() -> Result<(), TestError> { annotated_inner() } #[kaptan_err_propagate::propagate] fn handles_inner_error() -> Result<(), TestError> { let _handled = fail().is_err(); Ok(()) } #[kaptan_err_propagate::propagate] fn returns_explicit_error(fail_at_boundary: bool) -> Result<(), TestError> { if fail_at_boundary { return Err(TestError::Failed); } Ok(()) } type AliasedResult = core::result::Result; #[kaptan_err_propagate::propagate] fn returns_alias() -> AliasedResult<()> { fail() } #[kaptan_err_propagate::propagate] fn reports_final_converted_error() -> Result<(), OuterError> { fail()?; Ok(()) } #[kaptan_err_propagate::propagate] fn reports_error_id() -> Result<(), ErrorId> { Err(ErrorId::new(65003)) } #[kaptan_err_propagate::propagate] async fn async_boundary() -> Result<(), TestError> { fail()?; Ok(()) } struct Service; impl Service { #[kaptan_err_propagate::propagate] fn method(&self, fail_at_boundary: bool) -> Result<(), TestError> { if fail_at_boundary { fail()?; } Ok(()) } } fn take_record() -> kaptan_err_propagate::ErrorRecord { kaptan_err_propagate::take().expect("escaping error must be reported") } #[test] fn reports_only_final_errors_leaving_annotated_boundaries() { while kaptan_err_propagate::take().is_some() {} assert_eq!(succeeds(), Ok(7)); assert!(kaptan_err_propagate::take().is_none()); assert_eq!(handles_inner_error(), Ok(())); assert!(kaptan_err_propagate::take().is_none()); assert_eq!(annotated_outer(), Err(TestError::Failed)); assert_eq!(take_record().id(), TestError::Failed.error_id().get()); assert_eq!(unannotated_outer(), Err(TestError::Failed)); assert_eq!(take_record().id(), TestError::Failed.error_id().get()); assert_eq!(returns_explicit_error(true), Err(TestError::Failed)); assert_eq!(take_record().id(), TestError::Failed.error_id().get()); assert_eq!(returns_alias(), Err(TestError::Failed)); assert_eq!(take_record().id(), TestError::Failed.error_id().get()); assert_eq!( reports_final_converted_error(), Err(OuterError::Converted(TestError::Failed)) ); assert_eq!( take_record().id(), OuterError::Converted(TestError::Failed).error_id().get() ); assert_eq!(reports_error_id(), Err(ErrorId::new(65003))); assert_eq!(take_record().id(), 65003); assert_eq!(Service.method(false), Ok(())); assert!(kaptan_err_propagate::take().is_none()); let mut future = core::pin::pin!(async_boundary()); let mut context = core::task::Context::from_waker(core::task::Waker::noop()); assert_eq!( core::future::Future::poll(future.as_mut(), &mut context), core::task::Poll::Ready(Err(TestError::Failed)) ); let record = take_record(); assert_eq!(record.id(), TestError::Failed.error_id().get()); assert!(record.raised_at().contains("propagate.rs:")); }