use core::fmt::Display;
pub use anyhow::{Context, Error, Result};
use bitcoin::{BlockHash, Txid};
use bitcoincore_rpc as rpc;
use crate::types::ScriptHash;
#[cfg(feature = "http")]
use warp::http::StatusCode;
#[derive(thiserror::Error, Debug)]
pub enum BwtError {
#[error("Reorg detected at height {0} (previous={1} current={2})")]
ReorgDetected(u32, BlockHash, BlockHash),
#[error("Transaction not found: {0}")]
TxNotFound(Txid),
#[error("Address or script hash not found: {0}")]
ScriptHashNotFound(ScriptHash),
#[error("Blocks unavailable due to pruning")]
PrunedBlocks,
#[error("The operation was canceled")]
Canceled,
#[error("Custom broadcast command failed with {0}")]
BroadcastCmdFailed(std::process::ExitStatus),
#[error("Transaction rejected: {0}")]
TxUnacceptable(String),
#[error("Error communicating with the Bitcoin RPC: {0}")]
RpcProtocol(rpc::Error),
#[error("Bitcoin RPC error code {}: {}", .0.code, .0.message)]
Rpc(rpc::jsonrpc::error::RpcError),
}
impl BwtError {
#[cfg(feature = "http")]
pub fn status_code(&self) -> StatusCode {
match self {
BwtError::ReorgDetected(..) => StatusCode::GONE,
BwtError::PrunedBlocks => StatusCode::GONE,
BwtError::TxNotFound(_) => StatusCode::NOT_FOUND,
BwtError::ScriptHashNotFound(_) => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
impl From<rpc::Error> for BwtError {
fn from(err: rpc::Error) -> Self {
use crate::util::bitcoincore_ext::RPC_MISC_ERROR;
if let rpc::Error::JsonRpc(rpc::jsonrpc::Error::Rpc(e)) = err {
match (e.code, e.message.as_str()) {
(RPC_MISC_ERROR, "Block not available (pruned data)") => BwtError::PrunedBlocks,
_ => BwtError::Rpc(e),
}
} else {
BwtError::RpcProtocol(err)
}
}
}
pub trait OptionExt<T> {
fn or_err<D>(self, context: D) -> Result<T>
where
D: Display + Send + Sync + 'static;
fn required(self) -> Result<T>;
}
impl<T> OptionExt<T> for Option<T> {
fn or_err<D>(self, context: D) -> Result<T>
where
D: Display + Send + Sync + 'static,
{
self.context(context)
}
fn required(self) -> Result<T> {
self.context("missing required option")
}
}
pub fn fmt_error_chain(err: &Error) -> String {
err.chain()
.map(|e| e.to_string())
.collect::<Vec<String>>()
.join(": ")
}