use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use amplify::hex;
use bc::{Outpoint, Txid, Vout};
use crate::txout::seal::{SealTxid, TxPtr};
use crate::txout::{CloseMethod, MethodParseError, TxoSeal, WitnessVoutError};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = dbc::LIB_NAME_BPCORE)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
pub struct ExplicitSeal<Id: SealTxid = Txid> {
pub method: CloseMethod,
pub txid: Id,
pub vout: Vout,
}
impl TryFrom<&ExplicitSeal<TxPtr>> for Outpoint {
type Error = WitnessVoutError;
#[inline]
fn try_from(reveal: &ExplicitSeal<TxPtr>) -> Result<Self, Self::Error> {
reveal
.txid
.map_to_outpoint(reveal.vout)
.ok_or(WitnessVoutError)
}
}
impl TryFrom<ExplicitSeal<TxPtr>> for Outpoint {
type Error = WitnessVoutError;
#[inline]
fn try_from(reveal: ExplicitSeal<TxPtr>) -> Result<Self, Self::Error> {
Outpoint::try_from(&reveal)
}
}
impl From<&ExplicitSeal<Txid>> for Outpoint {
fn from(seal: &ExplicitSeal<Txid>) -> Self { Outpoint::new(seal.txid, seal.vout) }
}
impl From<ExplicitSeal<Txid>> for Outpoint {
fn from(seal: ExplicitSeal<Txid>) -> Self { Outpoint::from(&seal) }
}
impl<Id: SealTxid> From<&Outpoint> for ExplicitSeal<Id> {
#[inline]
fn from(outpoint: &Outpoint) -> Self {
Self {
method: CloseMethod::TapretFirst,
txid: outpoint.txid.into(),
vout: outpoint.vout,
}
}
}
impl<Id: SealTxid> From<Outpoint> for ExplicitSeal<Id> {
#[inline]
fn from(outpoint: Outpoint) -> Self { ExplicitSeal::from(&outpoint) }
}
impl<Id: SealTxid> TxoSeal for ExplicitSeal<Id> {
#[inline]
fn method(&self) -> CloseMethod { self.method }
#[inline]
fn txid(&self) -> Option<Txid> { self.txid.txid() }
#[inline]
fn vout(&self) -> Vout { self.vout }
#[inline]
fn outpoint(&self) -> Option<Outpoint> { self.txid.map_to_outpoint(self.vout) }
#[inline]
fn txid_or(&self, default_txid: Txid) -> Txid { self.txid.txid_or(default_txid) }
#[inline]
fn outpoint_or(&self, default_txid: Txid) -> Outpoint {
Outpoint::new(self.txid.txid_or(default_txid), self.vout)
}
}
impl<Id: SealTxid> ExplicitSeal<Id> {
#[inline]
pub fn new(method: CloseMethod, outpoint: Outpoint) -> ExplicitSeal<Id> {
Self {
method,
txid: Id::from(outpoint.txid),
vout: outpoint.vout,
}
}
#[inline]
pub fn with(method: CloseMethod, txid: Id, vout: impl Into<Vout>) -> ExplicitSeal<Id> {
ExplicitSeal {
method,
txid,
vout: vout.into(),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Display, Error, From)]
#[display(doc_comments)]
pub enum ParseError {
MethodRequired,
TxidRequired,
#[display(inner)]
#[from]
WrongMethod(MethodParseError),
WrongTxid(hex::Error),
WrongVout,
WrongStructure,
}
impl<Id: SealTxid> FromStr for ExplicitSeal<Id> {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split = s.split(&[':', '#'][..]);
match (split.next(), split.next(), split.next(), split.next()) {
(Some("~"), ..) | (Some(""), ..) => Err(ParseError::MethodRequired),
(Some(_), Some(""), ..) => Err(ParseError::TxidRequired),
(Some(method), Some(txid), Some(vout), None) => Ok(ExplicitSeal {
method: method.parse()?,
txid: Id::from_str(txid).map_err(ParseError::WrongTxid)?,
vout: vout.parse().map_err(|_| ParseError::WrongVout)?,
}),
_ => Err(ParseError::WrongStructure),
}
}
}
impl<Id: SealTxid> Display for ExplicitSeal<Id> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.method, self.txid, self.vout,)
}
}