outcome.rs (4521B)
1 #![forbid(unsafe_code)] 2 3 use serde::{Deserialize, Serialize}; 4 5 #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] 6 pub enum RadrootsRelayOutcomeKind { 7 Accepted, 8 DuplicateAccepted, 9 Blocked, 10 RateLimited, 11 Invalid, 12 PowRequired, 13 Restricted, 14 AuthRequired, 15 Muted, 16 Unsupported, 17 PaymentRequired, 18 Error, 19 Timeout, 20 ConnectionFailed, 21 RelayUrlRejected, 22 SkippedAlreadyAccepted, 23 Unknown, 24 } 25 26 impl RadrootsRelayOutcomeKind { 27 pub fn counts_toward_quorum(self) -> bool { 28 matches!( 29 self, 30 Self::Accepted | Self::DuplicateAccepted | Self::SkippedAlreadyAccepted 31 ) 32 } 33 34 pub fn is_retryable(self) -> bool { 35 matches!( 36 self, 37 Self::RateLimited 38 | Self::PowRequired 39 | Self::AuthRequired 40 | Self::Error 41 | Self::Timeout 42 | Self::ConnectionFailed 43 | Self::Unknown 44 ) 45 } 46 47 pub fn is_terminal_failure(self) -> bool { 48 matches!( 49 self, 50 Self::Blocked 51 | Self::Invalid 52 | Self::Restricted 53 | Self::Muted 54 | Self::Unsupported 55 | Self::PaymentRequired 56 | Self::RelayUrlRejected 57 ) 58 } 59 } 60 61 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] 62 pub struct RadrootsRelayOutcome { 63 pub kind: RadrootsRelayOutcomeKind, 64 pub message: Option<String>, 65 } 66 67 impl RadrootsRelayOutcome { 68 pub fn accepted() -> Self { 69 Self { 70 kind: RadrootsRelayOutcomeKind::Accepted, 71 message: None, 72 } 73 } 74 75 pub fn duplicate_accepted(message: impl Into<String>) -> Self { 76 Self { 77 kind: RadrootsRelayOutcomeKind::DuplicateAccepted, 78 message: Some(message.into()), 79 } 80 } 81 82 pub fn connection_failed(message: impl Into<String>) -> Self { 83 Self { 84 kind: RadrootsRelayOutcomeKind::ConnectionFailed, 85 message: Some(message.into()), 86 } 87 } 88 89 pub fn timeout(message: impl Into<String>) -> Self { 90 Self { 91 kind: RadrootsRelayOutcomeKind::Timeout, 92 message: Some(message.into()), 93 } 94 } 95 96 pub fn relay_url_rejected(message: impl Into<String>) -> Self { 97 Self { 98 kind: RadrootsRelayOutcomeKind::RelayUrlRejected, 99 message: Some(message.into()), 100 } 101 } 102 103 pub fn skipped_already_accepted(message: impl Into<String>) -> Self { 104 Self { 105 kind: RadrootsRelayOutcomeKind::SkippedAlreadyAccepted, 106 message: Some(message.into()), 107 } 108 } 109 110 pub fn classify(message: impl AsRef<str>) -> Self { 111 let message = message.as_ref().trim(); 112 let lower = message.to_ascii_lowercase(); 113 let kind = if lower.starts_with("duplicate:") { 114 RadrootsRelayOutcomeKind::DuplicateAccepted 115 } else if lower.starts_with("blocked:") { 116 RadrootsRelayOutcomeKind::Blocked 117 } else if lower.starts_with("rate-limited:") { 118 RadrootsRelayOutcomeKind::RateLimited 119 } else if lower.starts_with("invalid:") { 120 RadrootsRelayOutcomeKind::Invalid 121 } else if lower.starts_with("pow:") { 122 RadrootsRelayOutcomeKind::PowRequired 123 } else if lower.starts_with("restricted:") { 124 RadrootsRelayOutcomeKind::Restricted 125 } else if lower.starts_with("auth-required:") { 126 RadrootsRelayOutcomeKind::AuthRequired 127 } else if lower.starts_with("mute:") { 128 RadrootsRelayOutcomeKind::Muted 129 } else if lower.starts_with("unsupported:") { 130 RadrootsRelayOutcomeKind::Unsupported 131 } else if lower.starts_with("payment-required:") { 132 RadrootsRelayOutcomeKind::PaymentRequired 133 } else if lower.starts_with("error:") { 134 RadrootsRelayOutcomeKind::Error 135 } else if lower.starts_with("timeout:") { 136 RadrootsRelayOutcomeKind::Timeout 137 } else { 138 RadrootsRelayOutcomeKind::Unknown 139 }; 140 Self { 141 kind, 142 message: Some(message.to_owned()), 143 } 144 } 145 146 pub fn counts_toward_quorum(&self) -> bool { 147 self.kind.counts_toward_quorum() 148 } 149 150 pub fn is_retryable(&self) -> bool { 151 self.kind.is_retryable() 152 } 153 154 pub fn is_terminal_failure(&self) -> bool { 155 self.kind.is_terminal_failure() 156 } 157 }