use std::fmt::{Display, Formatter};
use std::str::FromStr;
use bitcoin::Txid;
use bitcoin::hashes::Hash;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::error::{Error, Result};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Ord, PartialOrd)]
pub struct InscriptionId {
pub txid: Txid,
pub index: u32,
}
impl Default for InscriptionId {
fn default() -> Self {
Self {
txid: Txid::all_zeros(),
index: 0,
}
}
}
impl InscriptionId {
pub fn new(txid: Txid, index: u32) -> Self {
Self { txid, index }
}
pub fn value(self) -> Vec<u8> {
let index = self.index.to_le_bytes();
let mut index_slice = index.as_slice();
while index_slice.last().copied() == Some(0) {
index_slice = &index_slice[0..index_slice.len() - 1];
}
self
.txid
.as_byte_array()
.iter()
.chain(index_slice)
.copied()
.collect()
}
pub fn from_value(value: &[u8]) -> Option<Self> {
if value.len() < Txid::LEN {
return None;
}
if value.len() > Txid::LEN + 4 {
return None;
}
let (txid, index) = value.split_at(Txid::LEN);
if let Some(last) = index.last() {
if index.len() != 4 && *last == 0 {
return None;
}
}
let txid = Txid::from_raw_hash(bitcoin::hashes::Hash::from_slice(txid).ok()?);
let index = [
index.first().copied().unwrap_or_default(),
index.get(1).copied().unwrap_or_default(),
index.get(2).copied().unwrap_or_default(),
index.get(3).copied().unwrap_or_default(),
];
let index = u32::from_le_bytes(index);
Some(Self { txid, index })
}
}
impl Display for InscriptionId {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}i{}", self.txid, self.index)
}
}
impl FromStr for InscriptionId {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if let Some((txid, index)) = s.split_once('i') {
let txid = Txid::from_str(txid)
.map_err(|_| Error::InvalidInscriptionId(format!("invalid txid: {}", txid)))?;
let index = index.parse::<u32>()
.map_err(|_| Error::InvalidInscriptionId(format!("invalid index: {}", index)))?;
Ok(Self { txid, index })
} else {
Err(Error::InvalidInscriptionId(format!("invalid inscription id format: {}", s)))
}
}
}
impl Serialize for InscriptionId {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for InscriptionId {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}