+/// Strategies available to retry payment path failures.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum Retry {
+ /// Max number of attempts to retry payment.
+ ///
+ /// Note that this is the number of *path* failures, not full payment retries. For multi-path
+ /// payments, if this is less than the total number of paths, we will never even retry all of the
+ /// payment's paths.
+ Attempts(usize),
+ #[cfg(not(feature = "no-std"))]
+ /// Time elapsed before abandoning retries for a payment.
+ Timeout(core::time::Duration),
+}
+
+impl Retry {
+ pub(crate) fn is_retryable_now(&self, attempts: &PaymentAttempts) -> bool {
+ match (self, attempts) {
+ (Retry::Attempts(max_retry_count), PaymentAttempts { count, .. }) => {
+ max_retry_count > count
+ },
+ #[cfg(all(not(feature = "no-std"), not(test)))]
+ (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
+ *max_duration >= std::time::Instant::now().duration_since(*first_attempted_at),
+ #[cfg(all(not(feature = "no-std"), test))]
+ (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) =>
+ *max_duration >= SinceEpoch::now().duration_since(*first_attempted_at),
+ }
+ }
+}
+
+pub(crate) type PaymentAttempts = PaymentAttemptsUsingTime<ConfiguredTime>;
+
+/// Storing minimal payment attempts information required for determining if a outbound payment can
+/// be retried.
+pub(crate) struct PaymentAttemptsUsingTime<T: Time> {
+ /// This count will be incremented only after the result of the attempt is known. When it's 0,
+ /// it means the result of the first attempt is not known yet.
+ pub(crate) count: usize,
+ /// This field is only used when retry is `Retry::Timeout` which is only build with feature std
+ first_attempted_at: T
+}
+
+#[cfg(not(any(feature = "no-std", test)))]
+type ConfiguredTime = std::time::Instant;
+#[cfg(feature = "no-std")]
+type ConfiguredTime = crate::util::time::Eternity;
+#[cfg(all(not(feature = "no-std"), test))]
+type ConfiguredTime = SinceEpoch;
+
+impl<T: Time> PaymentAttemptsUsingTime<T> {
+ pub(crate) fn new() -> Self {
+ PaymentAttemptsUsingTime {
+ count: 0,
+ first_attempted_at: T::now()
+ }
+ }
+}
+
+impl<T: Time> Display for PaymentAttemptsUsingTime<T> {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+ #[cfg(feature = "no-std")]
+ return write!(f, "attempts: {}", self.count);
+ #[cfg(not(feature = "no-std"))]
+ return write!(
+ f,
+ "attempts: {}, duration: {}s",
+ self.count,
+ T::now().duration_since(self.first_attempted_at).as_secs()
+ );
+ }
+}
+