- Removes the TAG_SIZE constant on all tags because each parser might use a different size. There is no minimum size for tags.

- Moves the tag parsing checks to use ensure.
- Explicitly writes matching methods to avoid hiding complexity beyond utility functions.
This commit is contained in:
Vitor Pamplona 2025-03-13 17:24:20 -04:00
parent 682dc62703
commit 66535e11ce
116 changed files with 807 additions and 440 deletions

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.header.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DownloadUrlTag {
companion object {
const val TAG_NAME = "download_url"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.header.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class StreamUrlTag {
companion object {
const val TAG_NAME = "stream_url"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -21,7 +21,9 @@
package com.vitorpamplona.quartz.experimental.audio.header.tags
import com.fasterxml.jackson.module.kotlin.readValue
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.jackson.EventMapper
import com.vitorpamplona.quartz.utils.ensure
class WaveformTag(
val wave: List<Int>,
@ -30,12 +32,12 @@ class WaveformTag(
companion object {
const val TAG_NAME = "waveform"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): WaveformTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
val wave = runCatching { EventMapper.mapper.readValue<List<Int>>(tag[1]) }.getOrNull()
if (wave.isNullOrEmpty()) return null
return WaveformTag(wave)

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.track.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class CoverTag {
companion object {
const val TAG_NAME = "cover"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.track.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class MediaTag {
companion object {
const val TAG_NAME = "media"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -23,9 +23,10 @@ package com.vitorpamplona.quartz.experimental.audio.track.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.isNotName
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
data class ParticipantTag(
@ -36,26 +37,27 @@ data class ParticipantTag(
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Tag): ParticipantTag? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ParticipantTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parseKey(tag: Tag): String? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun assemble(
pubkey: HexKey,
relayHint: String?,
relayHint: String? = null,
) = arrayOfNotNull(TAG_NAME, pubkey, relayHint)
}
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.track.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class PriceTag {
companion object {
const val TAG_NAME = "price"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.audio.track.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class TypeTag {
companion object {
const val TAG_NAME = "c"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,16 +20,19 @@
*/
package com.vitorpamplona.quartz.experimental.bounties
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
import java.math.BigDecimal
class RewardTag {
companion object {
const val TAG_NAME = "reward"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): BigDecimal? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return runCatching { BigDecimal(tag[1]) }.getOrNull()
}

View File

@ -24,7 +24,7 @@ import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag
import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER
fun MarkedETag.Companion.parseFork(tag: Array<String>): MarkedETag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag.size < 4 || tag[0] != "e") return null
if (tag[ORDER_MARKER] != MARKER.FORK.code) return null
// ["e", id hex, relay hint, marker, pubkey]
return MarkedETag(

View File

@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.experimental.interactiveStories.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ReadStatusTag {
enum class STATUS(
val value: String,
@ -34,11 +37,12 @@ class ReadStatusTag {
companion object {
const val TAG_NAME = "status"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -60,7 +60,6 @@ data class RootSceneTag(
companion object {
const val TAG_NAME = "A"
const val TAG_SIZE = 2
fun assembleATagId(
kind: Int,

View File

@ -35,7 +35,6 @@ class StoryOptionTag(
companion object {
const val TAG_NAME = "option"
const val TAG_SIZE = 3
@JvmStatic
fun parse(tag: Array<String>): StoryOptionTag? {

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.nns.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class IPv4Tag {
companion object {
const val TAG_NAME = "ip4"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.nns.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class IPv6Tag {
companion object {
const val TAG_NAME = "ip6"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.nns.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class VersionTag {
companion object {
const val TAG_NAME = "version"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class PetnameTag {
companion object {
const val TAG_NAME = "petname"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class RankTag {
companion object {
const val TAG_NAME = "rank"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toIntOrNull()
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.relationshipStatus.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class SummaryTag {
companion object {
const val TAG_NAME = "summary"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.zapPolls.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ClosedAtTag {
companion object {
const val TAG_NAME = "closed_at"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,16 +20,19 @@
*/
package com.vitorpamplona.quartz.experimental.zapPolls.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
import kotlin.math.round
class ConsensusThresholdTag {
companion object {
const val TAG_NAME = "consensus_threshold"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Double? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()?.toDouble()?.div(100)
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.zapPolls.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class MaximumTag {
companion object {
const val TAG_NAME = "value_maximum"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.experimental.zapPolls.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class MinimumTag {
companion object {
const val TAG_NAME = "value_minimum"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.experimental.zapPolls.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class PollOptionTag(
val index: Int,
val descriptor: String,
@ -28,11 +31,13 @@ class PollOptionTag(
companion object {
const val TAG_NAME = "poll_option"
const val TAG_SIZE = 3
@JvmStatic
fun parse(tag: Array<String>): PollOptionTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
ensure(tag[2].isNotEmpty()) { return null }
val index = tag[1].toIntOrNull() ?: return null

View File

@ -27,64 +27,12 @@ typealias Tag = Array<String>
*/
fun Tag.has(index: Int) = size > index
/**
* Returns if the Tag has less than $index elements.
*/
fun Tag.lacks(index: Int) = size <= index
fun Tag.name() = this[0]
fun Tag.value() = this[1]
fun Tag.hasValue() = this[1].isNotEmpty()
fun Tag.hasValue() = if (has(1)) this[1].isNotEmpty() else false
fun Tag.nameOrNull() = if (size > 0) name() else null
fun Tag.nameOrNull() = if (has(0)) this[0] else null
fun Tag.valueOrNull() = if (size > 1) value() else null
fun Tag.isNameUnsafe(name: String): Boolean = name() == name
fun Tag.isValueUnsafe(value: String): Boolean = value() == value
fun Tag.isValueInUnsafe(values: Set<String>): Boolean = value() in values
fun Tag.match(name: String): Boolean = if (size > 0) isNameUnsafe(name) else false
fun Tag.isValue(value: String): Boolean = if (size > 1) isValueUnsafe(value) else false
fun Tag.match(
name: String,
value: String,
minSize: Int,
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueUnsafe(value) else false
fun Tag.match(
name: String,
values: Set<String>,
minSize: Int,
): Boolean = if (size >= minSize) isNameUnsafe(name) && isValueInUnsafe(values) else false
fun Tag.match(
name: String,
minSize: Int,
): Boolean = if (size >= minSize) isNameUnsafe(name) else false
fun Tag.isNotName(
name: String,
minSize: Int,
): Boolean = !match(name, minSize)
fun Tag.matchAndHasValue(
name: String,
minSize: Int,
): Boolean = if (size >= minSize) isNameUnsafe(name) && hasValue() else false
fun Tag.valueIfMatches(
name: String,
minSize: Int,
): String? = if (match(name, minSize)) value() else null
fun Tag.valueToIntIfMatches(
name: String,
minSize: Int,
): Int? = if (match(name, minSize)) value().toIntOrNull() else null
fun Tag.valueOrNull() = if (has(1)) this[1] else null

View File

@ -35,7 +35,7 @@ class TagArrayBuilder<T : IEvent> {
tagName: String,
tagValue: String,
): TagArrayBuilder<T> {
tagList[tagName]?.removeIf { it.value() == tagValue }
tagList[tagName]?.removeIf { it.valueOrNull() == tagValue }
if (tagList[tagName]?.isEmpty() == true) {
tagList.remove(tagName)
}
@ -46,18 +46,8 @@ class TagArrayBuilder<T : IEvent> {
predicate: (Tag, Tag) -> Boolean,
toCompare: Tag,
): TagArrayBuilder<T> {
tagList[toCompare.name()]?.removeIf { predicate(it, toCompare) }
if (tagList[toCompare.name()]?.isEmpty() == true) {
tagList.remove(toCompare.name())
}
return this
}
fun removeIf(
tagName: String,
tagValue: String,
): TagArrayBuilder<T> {
tagList[tagName]?.removeIf { it.value() == tagValue }
val tagName = toCompare.nameOrNull() ?: return this
tagList[tagName]?.removeIf { predicate(it, toCompare) }
if (tagList[tagName]?.isEmpty() == true) {
tagList.remove(tagName)
}

View File

@ -23,9 +23,6 @@ package com.vitorpamplona.quartz.nip01Core.tags.addressables
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.name
import com.vitorpamplona.quartz.nip01Core.core.value
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
@ -57,40 +54,44 @@ data class ATag(
companion object {
const val TAG_NAME = "a"
const val TAG_SIZE = 2
@JvmStatic
fun isTagged(tag: Array<String>) = tag.size >= 2 && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun isSameAddress(
tag1: Array<String>,
tag2: Array<String>,
) = tag1.match(tag2.name(), tag2.value(), TAG_SIZE)
): Boolean {
ensure(tag1.has(1)) { return false }
ensure(tag2.has(1)) { return false }
ensure(tag1[0] == tag2[0]) { return false }
ensure(tag1[1] == tag2[1]) { return false }
return true
}
@JvmStatic
fun isTagged(
tag: Array<String>,
addressId: String,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == addressId
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
@JvmStatic
fun isTagged(
tag: Array<String>,
address: ATag,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == address.toTag()
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == address.toTag()
@JvmStatic
fun isIn(
tag: Array<String>,
addressIds: Set<String>,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in addressIds
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
@JvmStatic
fun isTaggedWithKind(
tag: Array<String>,
kind: String,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
) = tag.has(1) && tag[0] == TAG_NAME && Address.isOfKind(tag[1], kind)
@JvmStatic
fun parseIfOfKind(

View File

@ -22,10 +22,12 @@ package com.vitorpamplona.quartz.nip01Core.tags.events
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@ -56,32 +58,38 @@ data class ETag(
companion object {
const val TAG_NAME = "e"
const val TAG_SIZE = 2
@JvmStatic
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].length == 64
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
@JvmStatic
fun isTagged(
tag: Array<String>,
eventId: HexKey,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == eventId
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
@JvmStatic
fun parse(tag: Array<String>): ETag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ETag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@JvmStatic
fun parseId(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return EventIdHint(tag[1], tag[2])
}

View File

@ -20,17 +20,21 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.hashtags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class HashtagTag {
companion object {
const val TAG_NAME = "t"
const val TAG_SIZE = 2
@JvmStatic
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,26 +20,30 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.kinds
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class KindTag {
companion object {
const val TAG_NAME = "k"
const val TAG_SIZE = 2
fun match(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
fun match(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME
fun isTagged(
tag: Array<String>,
kind: String,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] == kind
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == kind
fun isIn(
tag: Array<String>,
kinds: Set<String>,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in kinds
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in kinds
@JvmStatic
fun parse(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toInt()
}

View File

@ -24,12 +24,14 @@ import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip19Bech32.entities.NProfile
import com.vitorpamplona.quartz.nip19Bech32.toNpub
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@ -50,24 +52,25 @@ data class PTag(
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
fun isTagged(
tag: Array<String>,
key: HexKey,
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] == key
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == key
@JvmStatic
fun parse(tag: Tag): PTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return PTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parseKey(tag: Array<String>): HexKey? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) {
Log.w("PTag", "Invalid `$TAG_NAME` value ${tag.joinToString(", ")}")
return null
}
@ -76,7 +79,10 @@ data class PTag(
@JvmStatic
fun parseAsHint(tag: Array<String>): PubKeyHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return PubKeyHint(tag[1], tag[2])
}

View File

@ -20,34 +20,38 @@
*/
package com.vitorpamplona.quartz.nip01Core.tags.references
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip96FileStorage.HttpUrlFormatter
import com.vitorpamplona.quartz.utils.ensure
class ReferenceTag {
companion object {
const val TAG_NAME = "r"
const val TAG_SIZE = 2
@JvmStatic
fun isTagged(
tag: Array<String>,
reference: String,
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] == reference
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] == reference
@JvmStatic
fun isIn(
tag: Array<String>,
references: Set<String>,
): Boolean = tag.size >= 2 && tag[0] == TAG_NAME && tag[1] in references
): Boolean = tag.has(1) && tag[0] == TAG_NAME && tag[1] in references
@JvmStatic
fun hasReference(tag: Array<String>): Boolean {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return false
ensure(tag.has(1)) { return false }
ensure(tag[0] == TAG_NAME) { return false }
return tag[1].isNotEmpty()
}
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -23,11 +23,13 @@ package com.vitorpamplona.quartz.nip02FollowList.tags
import android.util.Log
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip19Bech32.decodePublicKey
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@ -56,20 +58,23 @@ data class ContactTag(
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
@JvmStatic
fun isTagged(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): ContactTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ContactTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@JvmStatic
fun parseValid(tag: Array<String>): ContactTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return try {
ContactTag(decodePublicKey(tag[1]).toHexKey(), tag.getOrNull(2), tag.getOrNull(3))
} catch (e: Exception) {
@ -80,13 +85,17 @@ data class ContactTag(
@JvmStatic
fun parseKey(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun parseValidKey(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return try {
decodePublicKey(tag[1]).toHexKey()
} catch (e: Exception) {
@ -97,7 +106,10 @@ data class ContactTag(
@JvmStatic
fun parseAsHint(tag: Array<String>): PubKeyHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return PubKeyHint(tag[1], tag[2])
}

View File

@ -22,11 +22,13 @@ package com.vitorpamplona.quartz.nip10Notes.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.events.GenericETag
import com.vitorpamplona.quartz.nip19Bech32.entities.NEvent
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@ -71,7 +73,7 @@ data class MarkedETag(
companion object {
const val TAG_NAME = "e"
const val TAG_SIZE = 4
private const val TAG_SIZE = 4
const val ORDER_NAME = 0
const val ORDER_EVT_ID = 1
@ -183,7 +185,10 @@ data class MarkedETag(
@JvmStatic
fun parseAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return EventIdHint(tag[1], tag[2])
}

View File

@ -48,9 +48,9 @@ fun List<String>.positionalMarkedTags(
}
}.map {
when (it) {
root -> arrayOf(tagName, it, "", "root")
replyingTo -> arrayOf(tagName, it, "", "reply")
forkedFrom -> arrayOf(tagName, it, "", "fork")
root -> arrayOf(tagName, it, "", MarkedETag.MARKER.ROOT)
replyingTo -> arrayOf(tagName, it, "", MarkedETag.MARKER.REPLY)
forkedFrom -> arrayOf(tagName, it, "", MarkedETag.MARKER.FORK)
else -> arrayOf(tagName, it)
}
}

View File

@ -20,8 +20,10 @@
*/
package com.vitorpamplona.quartz.nip13Pow.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
class PoWTag(
@ -34,20 +36,23 @@ class PoWTag(
companion object {
const val TAG_NAME = "nonce"
const val TAG_SIZE = 2
@JvmStatic
fun hasTagWithContent(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun hasTagWithContent(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): PoWTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return PoWTag(tag[1], tag.getOrNull(2)?.toIntOrNull())
}
@JvmStatic
fun parseCommitment(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag.getOrNull(2)?.toIntOrNull()
}

View File

@ -20,17 +20,21 @@
*/
package com.vitorpamplona.quartz.nip14Subject
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class SubjectTag {
companion object {
const val TAG_NAME = "subject"
const val TAG_SIZE = 2
@JvmStatic
fun hasTagWithContent(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1].isNotEmpty()
fun hasTagWithContent(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,17 +20,21 @@
*/
package com.vitorpamplona.quartz.nip17Dm.files.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class EncryptionAlgo {
companion object {
const val TAG_NAME = "encryption-algorithm"
const val TAG_SIZE = 2
@JvmStatic
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
fun isTag(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,20 +20,23 @@
*/
package com.vitorpamplona.quartz.nip17Dm.files.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.utils.ensure
class EncryptionKey {
companion object {
const val TAG_NAME = "decryption-key"
const val TAG_SIZE = 2
@JvmStatic
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME
@JvmStatic
fun parse(tag: Array<String>): ByteArray? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return runCatching { tag[1].hexToByteArray() }.getOrNull()
}

View File

@ -20,20 +20,23 @@
*/
package com.vitorpamplona.quartz.nip17Dm.files.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.hexToByteArray
import com.vitorpamplona.quartz.nip01Core.core.toHexKey
import com.vitorpamplona.quartz.utils.ensure
class EncryptionNonce {
companion object {
const val TAG_NAME = "decryption-nonce"
const val TAG_SIZE = 2
@JvmStatic
fun isTag(tag: Array<String>) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME
fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME
@JvmStatic
fun parse(tag: Array<String>): ByteArray? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return runCatching { tag[1].hexToByteArray() }.getOrNull()
}

View File

@ -20,19 +20,23 @@
*/
package com.vitorpamplona.quartz.nip17Dm.files.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class FileTypeTag {
companion object {
const val TAG_NAME = "file-type"
const val TAG_SIZE = 2
fun isIn(
tag: Array<String>,
mimeTypes: Set<String>,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in mimeTypes
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in mimeTypes
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -59,13 +59,13 @@ data class QAddressableTag(
override fun toTagArray() = assemble(address, relay)
companion object {
const val TAG_SIZE = 2
const val TAG_NAME = "q"
@JvmStatic
fun parse(tag: Array<String>): QAddressableTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == QTag.TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length != 64) { return null }
val address = Address.parse(tag[1]) ?: return null
return QAddressableTag(address, tag.getOrNull(2))
}
@ -76,12 +76,12 @@ data class QAddressableTag(
pubKeyHex: HexKey,
dTag: String,
relay: String?,
) = arrayOfNotNull(QTag.TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay)
) = arrayOfNotNull(TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay)
@JvmStatic
fun assemble(
address: Address,
relay: String?,
) = arrayOfNotNull(QTag.TAG_NAME, address.toValue(), relay)
) = arrayOfNotNull(TAG_NAME, address.toValue(), relay)
}
}

View File

@ -22,8 +22,10 @@ package com.vitorpamplona.quartz.nip18Reposts.quotes
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable
@ -47,11 +49,13 @@ data class QEventTag(
override fun toTagArray() = assemble(eventId, relay, author)
companion object {
const val TAG_SIZE = 2
const val TAG_NAME = "q"
@JvmStatic
fun parse(tag: Array<String>): QEventTag? {
if (tag.size < TAG_SIZE || tag[0] != QTag.TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@ -60,6 +64,6 @@ data class QEventTag(
eventId: HexKey,
relay: String?,
author: HexKey?,
) = arrayOfNotNull(QTag.TAG_NAME, eventId, relay, author)
) = arrayOfNotNull(TAG_NAME, eventId, relay, author)
}
}

View File

@ -20,35 +20,46 @@
*/
package com.vitorpamplona.quartz.nip18Reposts.quotes
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.ensure
interface QTag {
fun toTagArray(): Array<String>
companion object {
const val TAG_NAME = "q"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): QTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
return if (tag[1].length == 64) {
QEventTag.parse(tag)
QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
} else {
QAddressableTag.parse(tag)
val address = Address.parse(tag[1]) ?: return null
QAddressableTag(address, tag.getOrNull(2))
}
}
@JvmStatic
fun parseEventAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return EventIdHint(tag[1], tag[2])
}
@JvmStatic
fun parseAddressAsHint(tag: Array<String>): AddressHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length != 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
ensure(!tag[1].contains(':')) { return null }
return AddressHint(tag[1], tag[2])
}
}

View File

@ -107,8 +107,8 @@ class CommentEvent(
?: tags.lastNotNullOfOrNull(RootEventTag::parseKey)
fun replyingToAddressId(): String? =
tags.lastNotNullOfOrNull(RootAddressTag::parseAddress)
?: tags.lastNotNullOfOrNull(ReplyAddressTag::parseAddress)
tags.lastNotNullOfOrNull(RootAddressTag::parseAddressId)
?: tags.lastNotNullOfOrNull(ReplyAddressTag::parseAddressId)
override fun replyingToAddressOrEvent(): HexKey? = replyingToAddressId() ?: replyingTo()

View File

@ -23,11 +23,11 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class ReplyAddressTag(
@ -38,23 +38,32 @@ class ReplyAddressTag(
companion object {
const val TAG_NAME = "a"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): ReplyAddressTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ReplyAddressTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parseAddressId(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): AddressHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return AddressHint(tag[1], tag[2])
}

View File

@ -23,10 +23,11 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
data class ReplyAuthorTag(
@ -37,28 +38,32 @@ data class ReplyAuthorTag(
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Tag): ReplyAuthorTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ReplyAuthorTag(tag[1], tag.getOrNull(2))
}
@JvmStatic
fun parseKey(tag: Tag): HexKey? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): PubKeyHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return PubKeyHint(tag[1], tag[2])
}

View File

@ -23,11 +23,12 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class ReplyEventTag(
@ -39,42 +40,53 @@ class ReplyEventTag(
companion object {
const val TAG_NAME = "e"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun isTagged(
tag: Array<String>,
eventId: String,
) = tag.match(TAG_NAME, eventId, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
@JvmStatic
fun isIn(
tag: Array<String>,
eventIds: Set<String>,
) = tag.match(TAG_NAME, eventIds, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
@JvmStatic
fun parse(tag: Array<String>): ReplyEventTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ReplyEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@JvmStatic
fun parseKey(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parseKey(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun parseValidKey(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(Hex.isHex(tag[1])) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return EventIdHint(tag[1], tag[2])
}

View File

@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class ReplyIdentifierTag {
companion object {
const val TAG_NAME = "i"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parse(tag: Tag): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(

View File

@ -21,36 +21,36 @@
package com.vitorpamplona.quartz.nip22Comments.tags
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.matchAndHasValue
import com.vitorpamplona.quartz.nip01Core.core.valueToIntIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.utils.ensure
class ReplyKindTag {
companion object {
const val TAG_NAME = "k"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
@JvmStatic
fun isTagged(tag: Tag) = tag.matchAndHasValue(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun isTagged(
tag: Tag,
kind: String,
) = tag.match(TAG_NAME, kind, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == kind
@JvmStatic
fun isIn(
tag: Tag,
kinds: Set<String>,
) = tag.match(TAG_NAME, kinds, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in kinds
@JvmStatic
fun parse(tag: Tag) = tag.valueToIntIfMatches(TAG_NAME, TAG_SIZE)
fun parse(tag: Tag): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(kind: String) = arrayOf(TAG_NAME, kind)

View File

@ -24,8 +24,6 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@ -40,26 +38,27 @@ class RootAddressTag(
companion object {
const val TAG_NAME = "A"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun isTagged(
tag: Array<String>,
addressId: String,
) = tag.match(TAG_NAME, addressId, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
@JvmStatic
fun isIn(
tag: Array<String>,
addressIds: Set<String>,
) = tag.match(TAG_NAME, addressIds, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
@JvmStatic
fun parse(tag: Array<String>): RootAddressTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return RootAddressTag(tag[1], tag.getOrNull(2))
}
@ -72,11 +71,19 @@ class RootAddressTag(
}
@JvmStatic
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parseAddressId(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): AddressHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length == 64 || !tag[1].contains(':') || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return AddressHint(tag[1], tag[2])
}

View File

@ -24,11 +24,11 @@ import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.PUBKEY_LENGTH
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import com.vitorpamplona.quartz.utils.ensure
@Immutable
data class RootAuthorTag(
@ -41,7 +41,7 @@ data class RootAuthorTag(
const val TAG_NAME = "P"
@JvmStatic
fun match(tag: Tag) = tag.size > 1 && tag[0] == TAG_NAME
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Tag): ReplyAuthorTag? {
@ -65,19 +65,13 @@ data class RootAuthorTag(
@JvmStatic
fun parseAsHint(tag: Array<String>): PubKeyHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != PUBKEY_LENGTH || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return PubKeyHint(tag[1], tag[2])
}
@OptIn(ExperimentalContracts::class)
inline fun ensure(
condition: Boolean,
exit: () -> Nothing,
) {
contract { returns() implies condition }
if (!condition) exit()
}
@JvmStatic
fun assemble(
pubkey: HexKey,

View File

@ -23,11 +23,12 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.events.EventReference
import com.vitorpamplona.quartz.utils.Hex
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class RootEventTag(
@ -39,42 +40,53 @@ class RootEventTag(
companion object {
const val TAG_NAME = "E"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun isTagged(
tag: Array<String>,
eventId: String,
) = tag.match(TAG_NAME, eventId, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == eventId
@JvmStatic
fun isIn(
tag: Array<String>,
eventIds: Set<String>,
) = tag.match(TAG_NAME, eventIds, TAG_SIZE)
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in eventIds
@JvmStatic
fun parse(tag: Array<String>): RootEventTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return RootEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@JvmStatic
fun parseKey(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parseKey(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}
@JvmStatic
fun parseValidKey(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(Hex.isHex(tag[1])) { return null }
return tag[1]
}
@JvmStatic
fun parseAsHint(tag: Array<String>): EventIdHint? {
if (tag.size < 3 || tag[0] != TAG_NAME || tag[1].length != 64 || tag[2].isEmpty()) return null
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return EventIdHint(tag[1], tag[2])
}

View File

@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class RootIdentifierTag {
companion object {
const val TAG_NAME = "I"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parse(tag: Tag): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(

View File

@ -21,20 +21,24 @@
package com.vitorpamplona.quartz.nip22Comments.tags
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.utils.ensure
class RootKindTag {
companion object {
const val TAG_NAME = "K"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Tag) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parse(tag: Tag): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(kind: String): Tag = arrayOf(TAG_NAME, kind)

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip23LongContent.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ImageTag {
companion object {
const val TAG_NAME = "image"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip23LongContent.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class PublishedAtTag {
companion object {
const val TAG_NAME = "published_at"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip23LongContent.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class SummaryTag {
companion object {
const val TAG_NAME = "summary"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip23LongContent.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class TitleTag {
companion object {
const val TAG_NAME = "title"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,18 +20,22 @@
*/
package com.vitorpamplona.quartz.nip31Alts
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class AltTag {
companion object {
const val TAG_NAME = "alt"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(altDescriptor: String = "") = arrayOf(TAG_NAME, altDescriptor)
fun assemble(altDescriptor: String) = arrayOf(TAG_NAME, altDescriptor)
}
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip34Git.repository.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class CloneTag {
companion object {
const val TAG_NAME = "clone"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip34Git.repository.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DescriptionTag {
companion object {
const val TAG_NAME = "description"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip34Git.repository.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class NameTag {
companion object {
const val TAG_NAME = "name"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip34Git.repository.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class WebTag {
companion object {
const val TAG_NAME = "web"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip35Torrents.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class BtihTag {
companion object {
const val TAG_NAME = "btih"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -21,7 +21,9 @@
package com.vitorpamplona.quartz.nip35Torrents.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip46RemoteSigner.getOrNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
class FileTag(
@ -32,18 +34,21 @@ class FileTag(
companion object {
const val TAG_NAME = "file"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): FileTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return FileTag(tag[1], tag.getOrNull(2)?.toLongOrNull())
}
@JvmStatic
fun parseBytes(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
return tag.getOrNull(2)?.toLongOrNull()
ensure(tag.has(2)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[2].isNotEmpty()) { return null }
return tag[2].toLongOrNull()
}
@JvmStatic

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip35Torrents.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class InfoHashTag {
companion object {
const val TAG_NAME = "x"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip35Torrents.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class TrackerTag {
companion object {
const val TAG_NAME = "tracker"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class CurrentParticipantsTag {
companion object {
const val TAG_NAME = "current_participants"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toIntOrNull()
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class EndsTag {
companion object {
const val TAG_NAME = "ends"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -23,9 +23,10 @@ package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.isNotName
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
enum class ROLE(
val code: String,
@ -43,40 +44,43 @@ data class ParticipantTag(
) : PubKeyReferenceTag {
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
fun isIn(
tag: Array<String>,
keys: Set<HexKey>,
) = tag.size >= TAG_SIZE && tag[0] == TAG_NAME && tag[1] in keys
) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in keys
@JvmStatic
fun isHost(tag: Tag): Boolean {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return false
if (tag[1].length != 64) return false
if (tag.getOrNull(3).equals(ROLE.HOST.code)) return true
return false
ensure(tag.has(3)) { return false }
ensure(tag[0] == TAG_NAME) { return false }
ensure(tag[1].length == 64) { return false }
ensure(tag[3] == ROLE.HOST.code) { return false }
return true
}
@JvmStatic
fun parse(tag: Tag): ParticipantTag? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ParticipantTag(tag[1], tag.getOrNull(2), tag.getOrNull(3), tag.getOrNull(4))
}
@JvmStatic
fun parseHost(tag: Tag): ParticipantTag? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
if (!tag.getOrNull(3).equals(ROLE.HOST.code)) return null
return ParticipantTag(tag[1], tag.getOrNull(2), tag.getOrNull(3), tag.getOrNull(4))
ensure(tag.has(3)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
ensure(tag[3] == ROLE.HOST.code) { return null }
return ParticipantTag(tag[1], tag[2], tag[3], tag.getOrNull(4))
}
@JvmStatic
fun parseKey(tag: Tag): String? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}

View File

@ -20,16 +20,20 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class RelayListTag(
val relayUrls: List<String>,
) {
companion object {
const val TAG_NAME = "relays"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): RelayListTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
val relays =
tag.mapIndexedNotNull { index, s ->
if (index == 0) null else s

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class StartsTag {
companion object {
const val TAG_NAME = "starts"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class StatusTag {
enum class STATUS(
val code: String,
@ -34,11 +37,12 @@ class StatusTag {
companion object {
const val TAG_NAME = "status"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class StreamingTag {
companion object {
const val TAG_NAME = "streaming"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip53LiveActivities.streaming.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class TotalParticipantsTag {
companion object {
const val TAG_NAME = "total_participants"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toIntOrNull()
}

View File

@ -31,7 +31,7 @@ class ZapRaiserTag(
fun toTagArray() = assemble(amountInSats)
companion object {
val TAG_NAME = "zapraiser"
const val TAG_NAME = "zapraiser"
@JvmStatic
fun parse(tags: Array<String>): ZapRaiserTag? {

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip71Video.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DurationTag {
companion object {
const val TAG_NAME = "duration"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Int? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toIntOrNull()
}

View File

@ -20,7 +20,9 @@
*/
package com.vitorpamplona.quartz.nip71Video.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
class SegmentTag(
val start: String, // HH:MM:SS.sss
@ -32,11 +34,14 @@ class SegmentTag(
companion object {
const val TAG_NAME = "segment"
const val TAG_SIZE = 4
@JvmStatic
fun parse(tag: Array<String>): SegmentTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(3)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
ensure(tag[2].isNotEmpty()) { return null }
ensure(tag[3].isNotEmpty()) { return null }
return SegmentTag(tag[1], tag[2], tag[3], tag.getOrNull(4))
}

View File

@ -21,8 +21,10 @@
package com.vitorpamplona.quartz.nip71Video.tags
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes
data class TextTrackTag(
@ -38,11 +40,12 @@ data class TextTrackTag(
companion object {
const val TAG_NAME = "text-track"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): TextTrackTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return TextTrackTag(tag[1], tag.getOrNull(2))
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DescriptionTag {
companion object {
const val TAG_NAME = "description"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,8 +20,10 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip94FileMetadata.tags.DimensionTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
class ImageTag(
val imageUrl: String,
@ -29,12 +31,12 @@ class ImageTag(
) {
companion object {
const val TAG_NAME = "image"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): ImageTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
if (tag[1].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
val dims = tag.getOrNull(2)?.let { DimensionTag.parse(it) }
return ImageTag(tag[1], dims)
}

View File

@ -23,9 +23,10 @@ package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.isNotName
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable
data class ModeratorTag(
@ -37,19 +38,20 @@ data class ModeratorTag(
companion object {
const val TAG_NAME = "p"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Tag): ModeratorTag? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return ModeratorTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
}
@JvmStatic
fun parseKey(tag: Tag): String? {
if (tag.isNotName(TAG_NAME, TAG_SIZE)) return null
if (tag[1].length != 64) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class NameTag {
companion object {
const val TAG_NAME = "name"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,7 +20,9 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
class RelayTag(
val url: String,
@ -30,11 +32,12 @@ class RelayTag(
companion object {
const val TAG_NAME = "relay"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): RelayTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return RelayTag(tag[1], tag.getOrNull(2))
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip72ModCommunities.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class RulesTag {
companion object {
const val TAG_NAME = "rules"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip75ZapGoals.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class AmountTag {
companion object {
const val TAG_NAME = "amount"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip75ZapGoals.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ClosedAtTag {
companion object {
const val TAG_NAME = "closed_at"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): Long? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1].toLongOrNull()
}

View File

@ -20,16 +20,20 @@
*/
package com.vitorpamplona.quartz.nip75ZapGoals.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class RelayListTag(
val relayUrls: List<String>,
) {
companion object {
const val TAG_NAME = "relays"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): RelayListTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
val relays =
tag.mapIndexedNotNull { index, s ->
if (index == 0) null else s

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip84Highlights.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class ContextTag {
companion object {
const val TAG_NAME = "context"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -21,6 +21,7 @@
package com.vitorpamplona.quartz.nip89AppHandlers.definition.tags
import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.nip89AppHandlers.PlatformType
import com.vitorpamplona.quartz.utils.arrayOfNotNull
@ -32,14 +33,10 @@ class PlatformLinkTag(
fun toTagArray() = assemble(platform, uri, entityType)
companion object {
const val TAG_SIZE = 3
@JvmStatic
fun match(tag: Tag): Boolean =
if (tag.size >= TAG_SIZE) {
tag[0] == PlatformType.IOS.code ||
tag[0] == PlatformType.WEB.code ||
tag[0] == PlatformType.ANDROID.code
if (tag.has(2)) {
tag[0] == PlatformType.IOS.code || tag[0] == PlatformType.WEB.code || tag[0] == PlatformType.ANDROID.code
} else {
false
}

View File

@ -43,7 +43,7 @@ class AppRecommendationEvent(
) : BaseAddressableEvent(id, pubKey, createdAt, KIND, tags, content, sig) {
fun recommendations() = tags.mapNotNull(RecommendationTag::parse)
fun recommendationAddresses() = tags.mapNotNull(RecommendationTag::parseAddress)
fun recommendationAddresses() = tags.mapNotNull(RecommendationTag::parseAddressId)
companion object {
const val KIND = 31989

View File

@ -23,8 +23,6 @@ package com.vitorpamplona.quartz.nip89AppHandlers.recommendation.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.core.match
import com.vitorpamplona.quartz.nip01Core.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@ -39,10 +37,9 @@ class RecommendationTag(
companion object {
const val TAG_NAME = "a"
const val TAG_SIZE = 2
@JvmStatic
fun match(tag: Tag) = tag.match(TAG_NAME, TAG_SIZE)
fun match(tag: Tag) = tag.has(1) && tag[0] == TAG_NAME && tag[1].isNotEmpty()
@JvmStatic
fun parse(tag: Array<String>): RecommendationTag? {
@ -54,7 +51,12 @@ class RecommendationTag(
}
@JvmStatic
fun parseAddress(tag: Array<String>) = tag.valueIfMatches(TAG_NAME, TAG_SIZE)
fun parseAddressId(tag: Array<String>): String? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}
@JvmStatic
fun assemble(

View File

@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.nip92IMeta
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class IMetaTag(
val url: String,
val properties: Map<String, List<String>>,
@ -38,10 +41,11 @@ class IMetaTag(
companion object {
const val TAG_NAME = "imeta"
const val ANCHOR_PROPERTY = "url"
const val TAG_SIZE = 2
fun parse(tag: Array<String>): IMetaTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
val allTags = parseIMeta(tag)
val url = allTags.get(ANCHOR_PROPERTY)?.firstOrNull()

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class BlurhashTag {
companion object {
const val TAG_NAME = "blurhash"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,6 +20,9 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DimensionTag(
val width: Int,
val height: Int,
@ -34,11 +37,12 @@ class DimensionTag(
companion object {
const val TAG_NAME = "dim"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): DimensionTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return parse(tag[1])
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class FallbackTag {
companion object {
const val TAG_NAME = "fallback"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class HashSha256Tag {
companion object {
const val TAG_NAME = "x"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME || tag[1].isEmpty()) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

View File

@ -21,7 +21,9 @@
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
class ImageTag(
val imageUrl: String,
@ -29,11 +31,12 @@ class ImageTag(
) {
companion object {
const val TAG_NAME = "image"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): ImageTag? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return ImageTag(tag[1], tag.getOrNull(2))
}

View File

@ -20,14 +20,18 @@
*/
package com.vitorpamplona.quartz.nip94FileMetadata.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class MagnetTag {
companion object {
const val TAG_NAME = "magnet"
const val TAG_SIZE = 2
@JvmStatic
fun parse(tag: Array<String>): String? {
if (tag.size < TAG_SIZE || tag[0] != TAG_NAME) return null
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null }
return tag[1]
}

Some files were not shown because too many files have changed in this diff Show More