- 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 package com.vitorpamplona.quartz.experimental.audio.header.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class DownloadUrlTag { class DownloadUrlTag {
companion object { companion object {
const val TAG_NAME = "download_url" const val TAG_NAME = "download_url"
const val TAG_SIZE = 2
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): String? { 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] return tag[1]
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,16 +20,19 @@
*/ */
package com.vitorpamplona.quartz.experimental.bounties package com.vitorpamplona.quartz.experimental.bounties
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
import java.math.BigDecimal import java.math.BigDecimal
class RewardTag { class RewardTag {
companion object { companion object {
const val TAG_NAME = "reward" const val TAG_NAME = "reward"
const val TAG_SIZE = 2
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): BigDecimal? { 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() 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 import com.vitorpamplona.quartz.nip10Notes.tags.MarkedETag.MARKER
fun MarkedETag.Companion.parseFork(tag: Array<String>): MarkedETag? { 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 if (tag[ORDER_MARKER] != MARKER.FORK.code) return null
// ["e", id hex, relay hint, marker, pubkey] // ["e", id hex, relay hint, marker, pubkey]
return MarkedETag( return MarkedETag(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -27,64 +27,12 @@ typealias Tag = Array<String>
*/ */
fun Tag.has(index: Int) = size > index 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.name() = this[0]
fun Tag.value() = this[1] 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.valueOrNull() = if (has(1)) this[1] 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

View File

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

View File

@ -23,9 +23,6 @@ package com.vitorpamplona.quartz.nip01Core.tags.addressables
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has 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.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.bytesUsedInMemory
@ -57,40 +54,44 @@ data class ATag(
companion object { companion object {
const val TAG_NAME = "a" const val TAG_NAME = "a"
const val TAG_SIZE = 2
@JvmStatic @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( fun isSameAddress(
tag1: Array<String>, tag1: Array<String>,
tag2: 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 @JvmStatic
fun isTagged( fun isTagged(
tag: Array<String>, tag: Array<String>,
addressId: 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 @JvmStatic
fun isTagged( fun isTagged(
tag: Array<String>, tag: Array<String>,
address: ATag, 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 @JvmStatic
fun isIn( fun isIn(
tag: Array<String>, tag: Array<String>,
addressIds: Set<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 @JvmStatic
fun isTaggedWithKind( fun isTaggedWithKind(
tag: Array<String>, tag: Array<String>,
kind: 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 @JvmStatic
fun parseIfOfKind( fun parseIfOfKind(

View File

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

View File

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

View File

@ -20,26 +20,30 @@
*/ */
package com.vitorpamplona.quartz.nip01Core.tags.kinds package com.vitorpamplona.quartz.nip01Core.tags.kinds
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class KindTag { class KindTag {
companion object { companion object {
const val TAG_NAME = "k" 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( fun isTagged(
tag: Array<String>, tag: Array<String>,
kind: 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( fun isIn(
tag: Array<String>, tag: Array<String>,
kinds: Set<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 @JvmStatic
fun parse(tag: Array<String>): Int? { 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() return tag[1].toInt()
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -59,13 +59,13 @@ data class QAddressableTag(
override fun toTagArray() = assemble(address, relay) override fun toTagArray() = assemble(address, relay)
companion object { companion object {
const val TAG_SIZE = 2 const val TAG_NAME = "q"
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): QAddressableTag? { fun parse(tag: Array<String>): QAddressableTag? {
ensure(tag.has(1)) { return null } ensure(tag.has(1)) { return null }
ensure(tag[0] == QTag.TAG_NAME) { return null } ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].isNotEmpty()) { return null } ensure(tag[1].length != 64) { return null }
val address = Address.parse(tag[1]) ?: return null val address = Address.parse(tag[1]) ?: return null
return QAddressableTag(address, tag.getOrNull(2)) return QAddressableTag(address, tag.getOrNull(2))
} }
@ -76,12 +76,12 @@ data class QAddressableTag(
pubKeyHex: HexKey, pubKeyHex: HexKey,
dTag: String, dTag: String,
relay: String?, relay: String?,
) = arrayOfNotNull(QTag.TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay) ) = arrayOfNotNull(TAG_NAME, Address.assemble(kind, pubKeyHex, dTag), relay)
@JvmStatic @JvmStatic
fun assemble( fun assemble(
address: Address, address: Address,
relay: String?, 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 androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey 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.arrayOfNotNull
import com.vitorpamplona.quartz.utils.bytesUsedInMemory import com.vitorpamplona.quartz.utils.bytesUsedInMemory
import com.vitorpamplona.quartz.utils.ensure
import com.vitorpamplona.quartz.utils.pointerSizeInBytes import com.vitorpamplona.quartz.utils.pointerSizeInBytes
@Immutable @Immutable
@ -47,11 +49,13 @@ data class QEventTag(
override fun toTagArray() = assemble(eventId, relay, author) override fun toTagArray() = assemble(eventId, relay, author)
companion object { companion object {
const val TAG_SIZE = 2 const val TAG_NAME = "q"
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): QEventTag? { 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)) return QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
} }
@ -60,6 +64,6 @@ data class QEventTag(
eventId: HexKey, eventId: HexKey,
relay: String?, relay: String?,
author: HexKey?, 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 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.AddressHint
import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint import com.vitorpamplona.quartz.nip01Core.hints.types.EventIdHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.ensure
interface QTag { interface QTag {
fun toTagArray(): Array<String> fun toTagArray(): Array<String>
companion object { companion object {
const val TAG_NAME = "q" const val TAG_NAME = "q"
const val TAG_SIZE = 2
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): QTag? { 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) { return if (tag[1].length == 64) {
QEventTag.parse(tag) QEventTag(tag[1], tag.getOrNull(2), tag.getOrNull(3))
} else { } else {
QAddressableTag.parse(tag) val address = Address.parse(tag[1]) ?: return null
QAddressableTag(address, tag.getOrNull(2))
} }
} }
@JvmStatic @JvmStatic
fun parseEventAsHint(tag: Array<String>): EventIdHint? { 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]) return EventIdHint(tag[1], tag[2])
} }
@JvmStatic @JvmStatic
fun parseAddressAsHint(tag: Array<String>): AddressHint? { 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]) return AddressHint(tag[1], tag[2])
} }
} }

View File

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

View File

@ -23,11 +23,11 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint import com.vitorpamplona.quartz.nip01Core.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable @Immutable
class ReplyAddressTag( class ReplyAddressTag(
@ -38,23 +38,32 @@ class ReplyAddressTag(
companion object { companion object {
const val TAG_NAME = "a" const val TAG_NAME = "a"
const val TAG_SIZE = 2
@JvmStatic @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 @JvmStatic
fun parse(tag: Array<String>): ReplyAddressTag? { 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)) return ReplyAddressTag(tag[1], tag.getOrNull(2))
} }
@JvmStatic @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 @JvmStatic
fun parseAsHint(tag: Array<String>): AddressHint? { 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]) return AddressHint(tag[1], tag[2])
} }

View File

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

View File

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

View File

@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable @Immutable
class ReplyIdentifierTag { class ReplyIdentifierTag {
companion object { companion object {
const val TAG_NAME = "i" const val TAG_NAME = "i"
const val TAG_SIZE = 2
@JvmStatic @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 @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 @JvmStatic
fun assemble( fun assemble(

View File

@ -21,36 +21,36 @@
package com.vitorpamplona.quartz.nip22Comments.tags package com.vitorpamplona.quartz.nip22Comments.tags
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.core.matchAndHasValue
import com.vitorpamplona.quartz.nip01Core.core.valueToIntIfMatches
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.utils.ensure
class ReplyKindTag { class ReplyKindTag {
companion object { companion object {
const val TAG_NAME = "k" const val TAG_NAME = "k"
const val TAG_SIZE = 2
@JvmStatic @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: Tag) = tag.matchAndHasValue(TAG_NAME, TAG_SIZE)
@JvmStatic @JvmStatic
fun isTagged( fun isTagged(
tag: Tag, tag: Tag,
kind: String, kind: String,
) = tag.match(TAG_NAME, kind, TAG_SIZE) ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == kind
@JvmStatic @JvmStatic
fun isIn( fun isIn(
tag: Tag, tag: Tag,
kinds: Set<String>, kinds: Set<String>,
) = tag.match(TAG_NAME, kinds, TAG_SIZE) ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in kinds
@JvmStatic @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 @JvmStatic
fun assemble(kind: String) = arrayOf(TAG_NAME, kind) 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.HexKey
import com.vitorpamplona.quartz.nip01Core.core.Tag import com.vitorpamplona.quartz.nip01Core.core.Tag
import com.vitorpamplona.quartz.nip01Core.core.has 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.hints.types.AddressHint
import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
@ -40,26 +38,27 @@ class RootAddressTag(
companion object { companion object {
const val TAG_NAME = "A" const val TAG_NAME = "A"
const val TAG_SIZE = 2
@JvmStatic @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 @JvmStatic
fun isTagged( fun isTagged(
tag: Array<String>, tag: Array<String>,
addressId: String, addressId: String,
) = tag.match(TAG_NAME, addressId, TAG_SIZE) ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] == addressId
@JvmStatic @JvmStatic
fun isIn( fun isIn(
tag: Array<String>, tag: Array<String>,
addressIds: Set<String>, addressIds: Set<String>,
) = tag.match(TAG_NAME, addressIds, TAG_SIZE) ) = tag.has(1) && tag[0] == TAG_NAME && tag[1] in addressIds
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): RootAddressTag? { 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)) return RootAddressTag(tag[1], tag.getOrNull(2))
} }
@ -72,11 +71,19 @@ class RootAddressTag(
} }
@JvmStatic @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 @JvmStatic
fun parseAsHint(tag: Array<String>): AddressHint? { 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]) 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.HexKey
import com.vitorpamplona.quartz.nip01Core.core.PUBKEY_LENGTH import com.vitorpamplona.quartz.nip01Core.core.PUBKEY_LENGTH
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.hints.types.PubKeyHint
import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag import com.vitorpamplona.quartz.nip01Core.tags.people.PubKeyReferenceTag
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import kotlin.contracts.ExperimentalContracts import com.vitorpamplona.quartz.utils.ensure
import kotlin.contracts.contract
@Immutable @Immutable
data class RootAuthorTag( data class RootAuthorTag(
@ -41,7 +41,7 @@ data class RootAuthorTag(
const val TAG_NAME = "P" const val TAG_NAME = "P"
@JvmStatic @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 @JvmStatic
fun parse(tag: Tag): ReplyAuthorTag? { fun parse(tag: Tag): ReplyAuthorTag? {
@ -65,19 +65,13 @@ data class RootAuthorTag(
@JvmStatic @JvmStatic
fun parseAsHint(tag: Array<String>): PubKeyHint? { 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]) return PubKeyHint(tag[1], tag[2])
} }
@OptIn(ExperimentalContracts::class)
inline fun ensure(
condition: Boolean,
exit: () -> Nothing,
) {
contract { returns() implies condition }
if (!condition) exit()
}
@JvmStatic @JvmStatic
fun assemble( fun assemble(
pubkey: HexKey, pubkey: HexKey,

View File

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

View File

@ -22,24 +22,28 @@ package com.vitorpamplona.quartz.nip22Comments.tags
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.core.valueIfMatches
import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash import com.vitorpamplona.quartz.nip01Core.tags.geohash.GeoHash
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId import com.vitorpamplona.quartz.nip73ExternalIds.GeohashId
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
@Immutable @Immutable
class RootIdentifierTag { class RootIdentifierTag {
companion object { companion object {
const val TAG_NAME = "I" const val TAG_NAME = "I"
const val TAG_SIZE = 2
@JvmStatic @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 @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 @JvmStatic
fun assemble( fun assemble(

View File

@ -21,20 +21,24 @@
package com.vitorpamplona.quartz.nip22Comments.tags package com.vitorpamplona.quartz.nip22Comments.tags
import com.vitorpamplona.quartz.nip01Core.core.Tag 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.core.valueIfMatches
import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId import com.vitorpamplona.quartz.nip73ExternalIds.ExternalId
import com.vitorpamplona.quartz.utils.ensure
class RootKindTag { class RootKindTag {
companion object { companion object {
const val TAG_NAME = "K" const val TAG_NAME = "K"
const val TAG_SIZE = 2
@JvmStatic @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 @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 @JvmStatic
fun assemble(kind: String): Tag = arrayOf(TAG_NAME, kind) fun assemble(kind: String): Tag = arrayOf(TAG_NAME, kind)

View File

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

View File

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

View File

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

View File

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

View File

@ -20,18 +20,22 @@
*/ */
package com.vitorpamplona.quartz.nip31Alts package com.vitorpamplona.quartz.nip31Alts
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class AltTag { class AltTag {
companion object { companion object {
const val TAG_NAME = "alt" const val TAG_NAME = "alt"
const val TAG_SIZE = 2
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): String? { 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] return tag[1]
} }
@JvmStatic @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 package com.vitorpamplona.quartz.nip34Git.repository.tags
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class CloneTag { class CloneTag {
companion object { companion object {
const val TAG_NAME = "clone" const val TAG_NAME = "clone"
const val TAG_SIZE = 2
@JvmStatic @JvmStatic
fun parse(tag: Array<String>): String? { 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] return tag[1]
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,6 +20,9 @@
*/ */
package com.vitorpamplona.quartz.nip92IMeta package com.vitorpamplona.quartz.nip92IMeta
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
class IMetaTag( class IMetaTag(
val url: String, val url: String,
val properties: Map<String, List<String>>, val properties: Map<String, List<String>>,
@ -38,10 +41,11 @@ class IMetaTag(
companion object { companion object {
const val TAG_NAME = "imeta" const val TAG_NAME = "imeta"
const val ANCHOR_PROPERTY = "url" const val ANCHOR_PROPERTY = "url"
const val TAG_SIZE = 2
fun parse(tag: Array<String>): IMetaTag? { 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 allTags = parseIMeta(tag)
val url = allTags.get(ANCHOR_PROPERTY)?.firstOrNull() val url = allTags.get(ANCHOR_PROPERTY)?.firstOrNull()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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