- Migrating a mix of tags from the old Tag array methods to the new Tag Object parsers.

- Starting to use fast methods for Tag array loops instead of the native kotlin ones.
This commit is contained in:
Vitor Pamplona
2025-05-14 19:19:29 -04:00
parent c0ecda94a3
commit dfdee1ae96
17 changed files with 130 additions and 145 deletions
@@ -20,17 +20,58 @@
*/
package com.vitorpamplona.quartz.nip01Core.core
import kotlin.collections.indices
typealias TagArray = Array<Array<String>>
fun <T : Event> TagArray.builder(initializer: TagArrayBuilder<T>.() -> Unit = {}) = TagArrayBuilder<T>().addAll(this).apply(initializer).build()
inline fun <T> Array<out T>.fastForEach(action: (T) -> Unit) {
for (index in indices) action(get(index))
}
inline fun <T> Array<out T>.fastAny(predicate: (T) -> Boolean): Boolean {
for (index in indices) if (predicate(get(index))) return true
return false
}
inline fun <T> Array<out T>.fastFirstOrNull(predicate: (T) -> Boolean): T? {
for (index in indices) {
if (predicate(get(index))) return get(index)
}
return null
}
inline fun <T, R : Any> Array<out T>.fastFirstNotNullOfOrNull(transform: (T) -> R?): R? {
for (index in indices) {
val result = transform(get(index))
if (result != null) {
return result
}
}
return null
}
inline fun <T, U, R : Any> Array<out T>.fastFirstNotNullOfOrNull(
transform: (T, U) -> R?,
extras: U,
): R? {
for (index in indices) {
val result = transform(get(index), extras)
if (result != null) {
return result
}
}
return null
}
/**
* Performs the given [action] on each tag that matches the given [tagName].
*/
fun TagArray.forEachTagged(
tagName: String,
action: (eventId: HexKey) -> Unit,
) = this.forEach {
) = this.fastForEach {
if (it.size > 1 && it[0] == tagName) {
action(it[1])
}
@@ -42,12 +83,7 @@ fun TagArray.forEachTagged(
fun TagArray.anyTagged(
tagName: String,
predicate: (tagValue: String) -> Boolean,
) = this.any { it.size > 1 && it[0] == tagName && predicate(it[1]) }
/**
* Returns `true` if at least one tag matches the given [tagName].
*/
fun TagArray.anyTagged(tagName: String) = this.any { it.size > 0 && it[0] == tagName }
) = this.fastAny { it.size > 1 && it[0] == tagName && predicate(it[1]) }
/**
* Returns `true` if at least one tag matches the given [tagName] and its value starts with a prefix
@@ -56,12 +92,12 @@ fun TagArray.anyTagWithValueStartingWith(
tagName: String,
valuePrefix: String,
ignoreCase: Boolean = true,
): Boolean = this.any { it.size > 1 && it[0] == tagName && it[1].startsWith(valuePrefix, ignoreCase) }
): Boolean = this.fastAny { it.size > 1 && it[0] == tagName && it[1].startsWith(valuePrefix, ignoreCase) }
/**
* Returns `true` if at least one tag matches the given name and has a value
*/
fun TagArray.hasTagWithContent(tagName: String) = this.any { it.size > 1 && it[0] == tagName }
fun TagArray.hasTagWithContent(tagName: String) = this.fastAny { it.size > 1 && it[0] == tagName }
/**
* Returns a list containing only the non-null results of applying the tag value to the given [transform] function
@@ -78,82 +114,20 @@ fun <R> TagArray.mapValueTagged(
}
}
/**
* Returns a list containing only the non-null results of applying the tag array to the given [transform] function
* to each tag that matches the [tagName]
*/
fun <R> TagArray.mapTagged(
tagName: String,
map: (tagValue: Array<String>) -> R,
) = this.mapNotNull {
if (it.size > 1 && it[0] == tagName) {
map(it)
} else {
null
}
}
/**
* Returns a list containing only the non-null tag values that match the [tagName]
*/
fun TagArray.mapValues(tagName: String) =
this.mapNotNull {
if (it.size > 1 && it[0] == tagName) {
it[1]
} else {
null
}
}
/**
* Returns the first non-null value produced by [transform] function being applied to all tags
* that match the [tagName]
*/
fun <R> TagArray.firstMappedTag(
tagName: String,
transform: (tagValue: Array<String>) -> R,
) = this.firstNotNullOfOrNull {
if (it.size > 1 && it[0] == tagName) {
transform(it)
} else {
null
}
}
/**
* Returns a list of tags that match the given [tagName].
*/
fun TagArray.filterByTag(tagName: String) = this.filter { it.size > 0 && it[0] == tagName }
/**
* Returns a list of tags that match the given [tagName] and have a tag value.
*/
fun TagArray.filterByTagWithValue(tagName: String) = this.filter { it.size > 1 && it[0] == tagName }
/**
* Returns the first tag that match the given [tagName] and have a tag value.
*/
fun TagArray.firstTag(key: String) = this.firstOrNull { it.size > 1 && it[0] == key }
/**
* Returns the first tag value that match the given [tagName] and have a tag value.
*/
fun TagArray.firstTagValue(key: String) = this.firstOrNull { it.size > 1 && it[0] == key }?.let { it[1] }
/**
* Returns the first tag value that match the given [tagName] and have a tag value as integer
*/
fun TagArray.firstTagValueAsInt(key: String) = this.firstOrNull { it.size > 1 && it[0] == key }?.let { it[1].toIntOrNull() }
fun TagArray.firstTagValue(key: String) = this.fastFirstOrNull { it.size > 1 && it[0] == key }?.let { it[1] }
/**
* Returns the first tag value that match the given [tagName] and have a tag value as long
*/
fun TagArray.firstTagValueAsLong(key: String) = this.firstOrNull { it.size > 1 && it[0] == key }?.let { it[1].toLongOrNull() }
fun TagArray.firstTagValueAsLong(key: String) = this.fastFirstOrNull { it.size > 1 && it[0] == key }?.let { it[1].toLongOrNull() }
/**
* Returns the first tag value that match any of the given [tagNames] and have a tag value
*/
fun TagArray.firstTagValueFor(vararg tagNames: String) = this.firstOrNull { it.size > 1 && it[0] in tagNames }?.let { it[1] }
fun TagArray.firstTagValueFor(vararg tagNames: String) = this.fastFirstOrNull { it.size > 1 && it[0] in tagNames }?.let { it[1] }
/**
* Returns `true` if at least one tag matches the given [tagName] and [tagValue]
@@ -162,7 +136,7 @@ fun TagArray.isTagged(
tagName: String,
tagValue: String,
ignoreCase: Boolean = false,
) = this.any { it.size > 1 && it[0] == tagName && it[1].equals(tagValue, ignoreCase) }
) = this.fastAny { it.size > 1 && it[0] == tagName && it[1].equals(tagValue, ignoreCase) }
/**
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
@@ -170,27 +144,16 @@ fun TagArray.isTagged(
fun TagArray.isAnyTagged(
tagName: String,
tagValues: Set<String>,
) = this.any { it.size > 1 && it[0] == tagName && it[1] in tagValues }
) = this.fastAny { it.size > 1 && it[0] == tagName && it[1] in tagValues }
fun <U> TagArray.any(
predicate: (Array<String>, U) -> Boolean,
extras: U,
): Boolean {
for (element in this) if (predicate(element, extras)) return true
return false
}
public inline fun <T, U, R : Any> Array<out T>.firstNotNullOfOrNull(
transform: (T, U) -> R?,
extras: U,
): R? {
for (element in this) {
val result = transform(element, extras)
if (result != null) {
return result
}
for (index in indices) {
if (predicate(get(index), extras)) return true
}
return null
return false
}
/**
@@ -199,7 +162,7 @@ public inline fun <T, U, R : Any> Array<out T>.firstNotNullOfOrNull(
fun TagArray.firstAnyLowercaseTaggedValue(
tagName: String,
tagValues: Set<String>,
) = this.firstOrNull { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }?.getOrNull(1)
) = this.fastFirstOrNull { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }?.getOrNull(1)
/**
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
@@ -207,15 +170,7 @@ fun TagArray.firstAnyLowercaseTaggedValue(
fun TagArray.isAnyLowercaseTagged(
tagName: String,
tagValues: Set<String>,
) = this.any { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }
/**
* Returns `true` if at least one tag matches the given [tagName] and is in [tagValues]
*/
fun TagArray.firstAnyTaggedValue(
tagName: String,
tagValues: Set<String>,
) = this.firstOrNull { it.size > 1 && it[0] == tagName && it[1] in tagValues }?.getOrNull(1)
) = this.fastAny { it.size > 1 && it[0] == tagName && it[1].lowercase() in tagValues }
/**
* Returns `true` if at least one tag has value that contains [text]
@@ -223,12 +178,12 @@ fun TagArray.firstAnyTaggedValue(
fun TagArray.tagValueContains(
text: String,
ignoreCase: Boolean = false,
) = this.any { it.size > 1 && it[1].contains(text, ignoreCase) }
) = this.fastAny { it.size > 1 && it[1].contains(text, ignoreCase) }
fun TagArray.containsAllTagNamesWithValues(names: Set<String>): Boolean {
val remaining = names.toMutableSet()
this.forEach {
this.fastForEach {
if (it.size > 1) {
remaining.remove(it[0])
}
@@ -22,12 +22,12 @@ package com.vitorpamplona.quartz.nip01Core.tags.addressables
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.any
import com.vitorpamplona.quartz.nip01Core.core.firstNotNullOfOrNull
import com.vitorpamplona.quartz.nip01Core.core.fastFirstNotNullOfOrNull
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
fun <R> TagArray.mapTaggedAddress(map: (address: String) -> R) = this.mapValueTagged(ATag.TAG_NAME, map)
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) = this.firstNotNullOfOrNull(ATag::parseIfIsIn, addressableNotes)
fun TagArray.firstIsTaggedAddressableNote(addressableNotes: Set<String>) = this.fastFirstNotNullOfOrNull(ATag::parseIfIsIn, addressableNotes)
fun TagArray.isTaggedAddressableNote(addressId: String) = this.any(ATag::isTagged, addressId)
@@ -35,12 +35,12 @@ fun TagArray.isTaggedAddressableNotes(addressIds: Set<String>) = this.any(ATag::
fun TagArray.isTaggedAddressableKind(kind: Int) = this.any(ATag::isTaggedWithKind, kind.toString())
fun TagArray.getTagOfAddressableKind(kind: Int) = this.firstNotNullOfOrNull(ATag::parseIfOfKind, kind.toString())
fun TagArray.getTagOfAddressableKind(kind: Int) = this.fastFirstNotNullOfOrNull(ATag::parseIfOfKind, kind.toString())
fun TagArray.taggedATags() = this.mapNotNull(ATag::parse)
fun TagArray.firstTaggedATag() = this.firstNotNullOfOrNull(ATag::parse)
fun TagArray.firstTaggedATag() = this.fastFirstNotNullOfOrNull(ATag::parse)
fun TagArray.taggedAddresses() = this.mapNotNull(ATag::parseAddress)
fun TagArray.firstTaggedAddress() = this.firstNotNullOfOrNull(ATag::parseAddress)
fun TagArray.firstTaggedAddress() = this.fastFirstNotNullOfOrNull(ATag::parseAddress)
@@ -21,15 +21,30 @@
package com.vitorpamplona.quartz.nip01Core.tags.geohash
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class GeoHash {
class GeoHashTag {
companion object {
const val TAG_NAME = "g"
@JvmStatic
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tags: Array<String>): String? {
ensure(tags.has(1)) { return null }
ensure(tags[0] == TAG_NAME) { return null }
return tags[1]
}
@JvmStatic
fun assembleSingle(geohash: String) = arrayOf(TAG_NAME, geohash)
@JvmStatic
fun geoMipMap(geohash: String): List<String> = geohash.indices.map { geohash.substring(0, it + 1) }.reversed()
fun geohashMipMap(geohash: String): TagArray = geoMipMap(geohash).map { arrayOf(TAG_NAME, it) }.toTypedArray()
fun geohashMipMap(geohash: String): TagArray = geoMipMap(geohash).map { assembleSingle(it) }.toTypedArray()
fun assemble(geohash: String) = geohashMipMap(geohash)
}
@@ -23,4 +23,4 @@ package com.vitorpamplona.quartz.nip01Core.tags.geohash
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
fun <T : Event> TagArrayBuilder<T>.geohash(tag: String) = addAll(GeoHash.assemble(tag))
fun <T : Event> TagArrayBuilder<T>.geohash(tag: String) = addAll(GeoHashTag.assemble(tag))
@@ -24,14 +24,13 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.anyTagWithValueStartingWith
import com.vitorpamplona.quartz.nip01Core.core.hasTagWithContent
import com.vitorpamplona.quartz.nip01Core.core.isAnyTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
fun TagArray.hasGeohashes() = this.hasTagWithContent(GeoHash.TAG_NAME)
fun TagArray.hasGeohashes() = this.hasTagWithContent(GeoHashTag.TAG_NAME)
fun TagArray.isTaggedGeoHashes(hashtags: Set<String>) = this.isAnyTagged(GeoHash.TAG_NAME, hashtags)
fun TagArray.isTaggedGeoHashes(hashtags: Set<String>) = this.isAnyTagged(GeoHashTag.TAG_NAME, hashtags)
fun TagArray.isTaggedGeoHash(hashtag: String) = this.anyTagWithValueStartingWith(GeoHash.TAG_NAME, hashtag)
fun TagArray.isTaggedGeoHash(hashtag: String) = this.anyTagWithValueStartingWith(GeoHashTag.TAG_NAME, hashtag)
fun TagArray.geohashes() = this.mapValues(GeoHash.TAG_NAME)
fun TagArray.geohashes() = this.mapNotNull(GeoHashTag::parse)
fun TagArray.getGeoHash(): String? = geohashes().maxByOrNull { it.length }?.ifBlank { null }
@@ -25,18 +25,16 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.anyTagged
import com.vitorpamplona.quartz.nip01Core.core.firstAnyLowercaseTaggedValue
import com.vitorpamplona.quartz.nip01Core.core.forEachTagged
import com.vitorpamplona.quartz.nip01Core.core.hasTagWithContent
import com.vitorpamplona.quartz.nip01Core.core.isAnyLowercaseTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
fun TagArray.forEachHashTag(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(HashtagTag.TAG_NAME, onEach)
fun TagArray.anyHashTag(onEach: (str: String) -> Boolean) = this.anyTagged(HashtagTag.TAG_NAME, onEach)
fun TagArray.hasHashtags() = this.hasTagWithContent(HashtagTag.TAG_NAME)
fun TagArray.hasHashtags() = this.any(HashtagTag::isTagged)
fun TagArray.hashtags() = this.mapValues(HashtagTag.TAG_NAME)
fun TagArray.hashtags() = this.mapNotNull(HashtagTag::parse)
fun TagArray.countHashtags() = this.count(HashtagTag::isTagged)
@@ -44,6 +44,13 @@ interface QTag {
}
}
@JvmStatic
fun parseKey(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return tag[1]
}
@JvmStatic
fun parseEventAsHint(tag: Array<String>): EventIdHint? {
ensure(tag.has(1)) { return null }
@@ -25,15 +25,14 @@ import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.forEachTagged
import com.vitorpamplona.quartz.nip01Core.core.isTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValueTagged
import com.vitorpamplona.quartz.nip01Core.core.mapValues
fun TagArray.forEachTaggedQuoteId(onEach: (eventId: HexKey) -> Unit) = this.forEachTagged(QTag.TAG_NAME, onEach)
fun <R> TagArray.mapTaggedQuoteId(map: (eventId: HexKey) -> R) = this.mapValueTagged(QTag.TAG_NAME, map)
fun TagArray.taggedQuotes() = this.mapNotNull(QTag.Companion::parse)
fun TagArray.taggedQuotes() = this.mapNotNull(QTag::parse)
fun TagArray.taggedQuoteIds() = this.mapValues(QTag.TAG_NAME)
fun TagArray.taggedQuoteIds() = this.mapNotNull(QTag::parseKey)
fun TagArray.firstTaggedQuote() = this.firstNotNullOfOrNull(QTag.Companion::parse)
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -54,7 +54,7 @@ class ReplyIdentifierTag {
@JvmStatic
fun assemble(id: ExternalId): List<Array<String>> =
when (id) {
is GeohashId -> GeoHash.geoMipMap(id.geohash).map { assemble(it, id.hint) }
is GeohashId -> GeoHashTag.geoMipMap(id.geohash).map { assemble(it, id.hint) }
else -> listOf(assemble(id.toScope(), id.hint()))
}
}
@@ -23,7 +23,7 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHashTag
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@@ -54,7 +54,7 @@ class RootIdentifierTag {
@JvmStatic
fun assemble(id: ExternalId): List<Array<String>> =
when (id) {
is GeohashId -> GeoHash.geoMipMap(id.geohash).map { assemble(it, id.hint) }
is GeohashId -> GeoHashTag.geoMipMap(id.geohash).map { assemble(it, id.hint) }
else -> listOf(assemble(id.toScope(), id.hint()))
}
}
@@ -48,7 +48,7 @@ data class EmojiUrlTag(
}
fun parse(tag: Array<String>): EmojiUrlTag? =
if (tag.size > 2 && tag[0] == "emoji") {
if (tag.size > 2 && tag[0] == TAG_NAME) {
EmojiUrlTag(tag[1], tag[2])
} else {
null
@@ -21,6 +21,5 @@
package com.vitorpamplona.quartz.nip30CustomEmoji
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.mapTagged
fun TagArray.emojis() = this.mapTagged(EmojiUrlTag.TAG_NAME) { EmojiUrlTag.parse(it) }
fun TagArray.emojis() = this.mapNotNull(EmojiUrlTag::parse)
@@ -21,7 +21,9 @@
package com.vitorpamplona.quartz.nip48ProxyTags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@@ -37,8 +39,12 @@ data class ProxyTag(
const val TAG_NAME = "proxy"
@JvmStatic
fun parse(tags: Array<String>): ProxyTag {
require(tags[0] == TAG_NAME)
fun isTagged(tag: Array<String>) = tag.has(2) && tag[0] == TAG_NAME && tag[1].isNotEmpty() && tag[2].isNotEmpty()
@JvmStatic
fun parse(tags: Array<String>): ProxyTag? {
ensure(tags.has(2)) { return null }
ensure(tags[0] == TAG_NAME) { return null }
return ProxyTag(tags[1], tags[2])
}
@@ -21,12 +21,11 @@
package com.vitorpamplona.quartz.nip48ProxyTags
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.firstTagValue
import com.vitorpamplona.quartz.nip01Core.core.hasTagWithContent
import com.vitorpamplona.quartz.nip01Core.core.mapValues
import com.vitorpamplona.quartz.nip01Core.core.fastAny
import com.vitorpamplona.quartz.nip01Core.core.fastFirstNotNullOfOrNull
fun TagArray.proxies() = this.mapValues(ProxyTag.TAG_NAME)
fun TagArray.proxies() = this.mapNotNull(ProxyTag::parse)
fun TagArray.firstProxy() = this.firstTagValue(ProxyTag.TAG_NAME)
fun TagArray.firstProxy() = this.fastFirstNotNullOfOrNull(ProxyTag::parse)
fun TagArray.hasProxy() = this.hasTagWithContent(ProxyTag.TAG_NAME)
fun TagArray.hasProxy() = this.fastAny(ProxyTag::isTagged)
@@ -26,10 +26,13 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.mapValues
import com.vitorpamplona.quartz.nip01Core.crypto.KeyPair
import com.vitorpamplona.quartz.nip01Core.signers.NostrSigner
import com.vitorpamplona.quartz.nip01Core.signers.NostrSignerInternal
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag
import com.vitorpamplona.quartz.nip01Core.tags.events.ETag.Companion.parseId
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag
import com.vitorpamplona.quartz.nip01Core.tags.people.PTag.Companion.parseKey
import com.vitorpamplona.quartz.nip31Alts.AltTag
import com.vitorpamplona.quartz.utils.TimeUtils
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@@ -47,9 +50,9 @@ class LnZapRequestEvent(
override fun countMemory(): Long = super.countMemory() + pointerSizeInBytes + (privateZapEvent?.countMemory() ?: 0)
fun zappedPost() = tags.mapValues("e")
fun zappedPost() = tags.mapNotNull(ETag::parseId)
fun zappedAuthor() = tags.mapValues("p")
fun zappedAuthor() = tags.mapNotNull(PTag::parseKey)
fun isPrivateZap() = tags.any { t -> t.size >= 2 && t[0] == "anon" && t[1].isNotBlank() }
@@ -21,9 +21,7 @@
package com.vitorpamplona.quartz.nip57Zaps.splits
import com.vitorpamplona.quartz.nip01Core.core.TagArray
import com.vitorpamplona.quartz.nip01Core.core.hasTagWithContent
import com.vitorpamplona.quartz.nip01Core.core.mapTagged
fun TagArray.hasZapSplitSetup() = this.hasTagWithContent(BaseZapSplitSetup.TAG_NAME)
fun TagArray.hasZapSplitSetup() = this.any(ZapSplitSetupParser::isTagged)
fun TagArray.zapSplitSetup(): List<BaseZapSplitSetup> = this.mapTagged(BaseZapSplitSetup.TAG_NAME) { ZapSplitSetupParser.parse(it) }
fun TagArray.zapSplitSetup(): List<BaseZapSplitSetup> = this.mapNotNull(ZapSplitSetupParser::parse)
@@ -20,11 +20,18 @@
*/
package com.vitorpamplona.quartz.nip57Zaps.splits
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ZapSplitSetupParser {
companion object {
@JvmStatic
fun isTagged(tags: Array<String>) = tags.has(1) && tags[0] == BaseZapSplitSetup.TAG_NAME
@JvmStatic
fun parse(tags: Array<String>): BaseZapSplitSetup? {
require(tags[0] == BaseZapSplitSetup.TAG_NAME)
ensure(tags.has(1)) { return null }
ensure(tags[0] == BaseZapSplitSetup.TAG_NAME) { return null }
val isLnAddress = tags[1].contains("@") || tags[1].startsWith("LNURL", true)
val weight = if (isLnAddress) 1.0 else (tags.getOrNull(3)?.toDoubleOrNull() ?: 0.0)