use base64::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, thiserror::Error)]
pub enum LndHodlInvoiceError {
#[error("Invalid HodlState")]
InvalidHodlState,
#[error("Invalid LndHodlInvoice")]
InvalidLndHodlInvoice,
#[error("InvoiceError: {0}")]
InvoiceError(lightning_invoice::ParseOrSemanticError),
#[error("Base64Error: {0}")]
Base64Error(#[from] base64::DecodeError),
#[error("SerdeJsonError: {0}")]
SerdeJsonError(#[from] serde_json::Error),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct LndHodlInvoice {
payment_addr: String,
payment_request: String,
add_index: String,
}
impl LndHodlInvoice {
#[must_use]
pub fn payment_hash(&self) -> Vec<u8> {
self.payment_addr.as_bytes().to_vec()
}
#[must_use]
pub fn payment_request(&self) -> String {
self.payment_request.clone()
}
#[must_use]
pub fn payment_hash_url_safe(&self) -> String {
let payment_hash = self.payment_hash();
BASE64_URL_SAFE.encode(payment_hash)
}
pub fn r_hash_url_safe(&self) -> Result<String, LndHodlInvoiceError> {
let r_hash = self
.payment_request
.parse::<lightning_invoice::Bolt11Invoice>()
.map_err(LndHodlInvoiceError::InvoiceError)?;
Ok(BASE64_URL_SAFE.encode(r_hash.payment_hash()))
}
pub fn sat_amount(&self) -> Result<u64, LndHodlInvoiceError> {
let bolt11 = self.payment_request.clone();
let bolt11 = bolt11
.parse::<lightning_invoice::Bolt11Invoice>()
.map_err(LndHodlInvoiceError::InvoiceError)?;
Ok(bolt11.amount_milli_satoshis().unwrap_or_default() / 1000)
}
}
impl std::str::FromStr for LndHodlInvoice {
type Err = LndHodlInvoiceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(serde_json::from_str(s)?)
}
}
impl TryFrom<LndHodlInvoice> for String {
type Error = LndHodlInvoiceError;
fn try_from(value: LndHodlInvoice) -> Result<Self, Self::Error> {
Ok(serde_json::to_string(&value)?)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "UPPERCASE")]
pub enum HodlState {
Open,
Accepted,
Canceled,
Settled,
}
impl std::str::FromStr for HodlState {
type Err = LndHodlInvoiceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(serde_json::from_str(s)?)
}
}
impl AsRef<str> for HodlState {
fn as_ref(&self) -> &str {
match self {
Self::Open => "OPEN",
Self::Accepted => "ACCEPTED",
Self::Canceled => "CANCELED",
Self::Settled => "SETTLED",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LndHodlInvoiceState {
settled: bool,
state: HodlState,
r_hash: String,
payment_request: String,
}
impl std::str::FromStr for LndHodlInvoiceState {
type Err = LndHodlInvoiceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(serde_json::from_str::<Self>(s)?)
}
}
impl TryInto<String> for LndHodlInvoiceState {
type Error = LndHodlInvoiceError;
fn try_into(self) -> Result<String, Self::Error> {
Ok(serde_json::to_string(&self)?)
}
}
impl LndHodlInvoiceState {
#[must_use]
pub const fn settled(&self) -> bool {
self.settled
}
#[must_use]
pub fn state(&self) -> HodlState {
self.state.clone()
}
#[must_use]
pub fn r_hash(&self) -> String {
self.r_hash.clone()
}
#[must_use]
pub fn r_hash_url_safe(&self) -> String {
BASE64_URL_SAFE.encode(self.r_hash.as_bytes())
}
#[must_use]
pub fn payment_request(&self) -> String {
self.payment_request.clone()
}
}