use super::transaction::Transaction;
use async_trait::async_trait;
use serde_json::Value;
#[derive(Debug, Clone)]
pub struct BroadcastResponse {
pub status: BroadcastStatus,
pub txid: String,
pub message: String,
pub competing_txs: Option<Vec<String>>,
}
impl BroadcastResponse {
pub fn success(txid: String, message: String) -> Self {
Self {
status: BroadcastStatus::Success,
txid,
message,
competing_txs: None,
}
}
pub fn success_with_competing(
txid: String,
message: String,
competing_txs: Vec<String>,
) -> Self {
Self {
status: BroadcastStatus::Success,
txid,
message,
competing_txs: Some(competing_txs),
}
}
}
#[derive(Debug, Clone)]
pub struct BroadcastFailure {
pub status: BroadcastStatus,
pub code: String,
pub txid: Option<String>,
pub description: String,
pub more: Option<Value>,
}
impl BroadcastFailure {
pub fn new(code: String, description: String) -> Self {
Self {
status: BroadcastStatus::Error,
code,
txid: None,
description,
more: None,
}
}
pub fn with_txid(code: String, txid: String, description: String) -> Self {
Self {
status: BroadcastStatus::Error,
code,
txid: Some(txid),
description,
more: None,
}
}
pub fn with_details(code: String, description: String, more: Value) -> Self {
Self {
status: BroadcastStatus::Error,
code,
txid: None,
description,
more: Some(more),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BroadcastStatus {
Success,
Error,
}
pub type BroadcastResult = Result<BroadcastResponse, BroadcastFailure>;
#[async_trait(?Send)]
pub trait Broadcaster: Send + Sync {
async fn broadcast(&self, tx: &Transaction) -> BroadcastResult;
async fn broadcast_many(&self, txs: Vec<Transaction>) -> Vec<BroadcastResult> {
let mut results = Vec::with_capacity(txs.len());
for tx in &txs {
results.push(self.broadcast(tx).await);
}
results
}
}
pub fn is_broadcast_success(result: &BroadcastResult) -> bool {
matches!(result, Ok(r) if r.status == BroadcastStatus::Success)
}
pub fn is_broadcast_failure(result: &BroadcastResult) -> bool {
result.is_err()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_broadcast_response_success() {
let response =
BroadcastResponse::success("abc123".to_string(), "Transaction accepted".to_string());
assert_eq!(response.status, BroadcastStatus::Success);
assert_eq!(response.txid, "abc123");
assert!(response.competing_txs.is_none());
}
#[test]
fn test_broadcast_failure_new() {
let failure = BroadcastFailure::new(
"INVALID_TX".to_string(),
"Transaction is invalid".to_string(),
);
assert_eq!(failure.status, BroadcastStatus::Error);
assert_eq!(failure.code, "INVALID_TX");
assert!(failure.txid.is_none());
}
#[test]
fn test_is_broadcast_success() {
let success: BroadcastResult = Ok(BroadcastResponse::success(
"abc".to_string(),
"ok".to_string(),
));
let failure: BroadcastResult = Err(BroadcastFailure::new(
"ERR".to_string(),
"error".to_string(),
));
assert!(is_broadcast_success(&success));
assert!(!is_broadcast_success(&failure));
assert!(!is_broadcast_failure(&success));
assert!(is_broadcast_failure(&failure));
}
}