Simplify Swift invoice handling with non-optional return types

- Mentions.swift: convert_invoice_description now returns non-optional
  InvoiceDescription, returning empty description for BOLT11 compliance
  (both description and description_hash are optional per spec)

- Block.swift, NdbBlock.swift, NostrEvent.swift, NoteContent.swift:
  Updated call sites to use non-optional invoice conversion

- InvoiceTests.swift: Added test for specific failing invoice

Signed-off-by: alltheseas <alltheseas@noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
alltheseas
2026-02-02 09:25:22 -06:00
committed by Daniel D’Aquino
parent 845089bed1
commit 1505a8f2e4
6 changed files with 66 additions and 53 deletions

View File

@@ -294,16 +294,19 @@ func format_msats(_ msat: Int64, locale: Locale = Locale.current) -> String {
return String(format: format, locale: locale, sats.decimalValue as NSDecimalNumber, formattedSats)
}
func convert_invoice_description(b11: ndb_invoice) -> InvoiceDescription? {
/// Extracts the description from a BOLT11 invoice.
/// Returns empty description if invoice has neither description nor description_hash,
/// as both fields are optional per BOLT11 spec.
func convert_invoice_description(b11: ndb_invoice) -> InvoiceDescription {
if let desc = b11.description {
return .description(String(cString: desc))
}
if var deschash = maybe_pointee(b11.description_hash) {
return .description_hash(Data(bytes: &deschash, count: 32))
}
return nil
return .description("")
}
func find_tag_ref(type: String, id: String, tags: [[String]]) -> Int? {

View File

@@ -839,9 +839,7 @@ func separate_invoices(ndb: Ndb, ev: NostrEvent, keypair: Keypair) -> [Invoice]?
let invoiceBlocks: [Invoice] = (try? blockGroup.reduce(initialResult: [Invoice](), { index, invoices, block in
switch block {
case .invoice(let invoice):
if let invoice = invoice.as_invoice() {
return .loopReturn(invoices + [invoice])
}
return .loopReturn(invoices + [invoice.as_invoice()])
default:
break
}

View File

@@ -79,8 +79,7 @@ extension Block {
guard let url = URL(string: block.as_str()) else { return nil }
self = .url(url)
case BLOCK_INVOICE:
guard let b = Block(invoice: block.block.invoice) else { return nil }
self = b
self = Block(invoice: block.block.invoice)
case BLOCK_MENTION_BECH32:
guard let b = Block(bech32: block.block.mention_bech32) else { return nil }
self = b
@@ -113,26 +112,20 @@ fileprivate extension Block {
}
fileprivate extension Block {
/// Failable initializer for the C-backed type `invoice_block_t`.
init?(invoice: ndb_invoice_block) {
guard let invoice = invoice_block_as_invoice(invoice) else { return nil }
self = .invoice(invoice)
/// Initializer for the C-backed type `invoice_block_t`.
init(invoice: ndb_invoice_block) {
self = .invoice(invoice_block_as_invoice(invoice))
}
}
func invoice_block_as_invoice(_ invoice: ndb_invoice_block) -> Invoice? {
/// Converts a C-backed invoice block to a Swift Invoice.
func invoice_block_as_invoice(_ invoice: ndb_invoice_block) -> Invoice {
let invstr = invoice.invstr.as_str()
let b11 = invoice.invoice
guard let description = convert_invoice_description(b11: b11) else {
return nil
}
let description = convert_invoice_description(b11: b11)
let amount: Amount = b11.amount == 0 ? .any : .specific(Int64(b11.amount))
return Invoice(description: description, amount: amount, string: invstr, expiry: b11.expiry, created_at: b11.timestamp)
}
fileprivate extension Block {