Maintain contributors with role attributor instead of exact role having it's own class

This commit is contained in:
Kgothatso Ngako
2026-07-17 21:28:39 +02:00
parent 803b9b6755
commit d2b64b5d91
20 changed files with 222 additions and 256 deletions

View File

@@ -24,13 +24,12 @@ import press.mantra.compose.database.dao.MantraChapterDao
import press.mantra.compose.database.dao.MantraChunkDao
import press.mantra.compose.database.dao.MantraDialectDao
import press.mantra.compose.database.dao.MantraTranslationArtifactVersionDao
import press.mantra.compose.database.dao.MantraTranslationArtifactVersionEditorDao
import press.mantra.compose.database.dao.MantraTranslationArtifactVersionContributorDao
import press.mantra.compose.database.dao.MantraTranslationChapterDao
import press.mantra.compose.database.dao.MantraTranslationChapterProofReaderDao
import press.mantra.compose.database.dao.MantraTranslationChapterTranslatorDao
import press.mantra.compose.database.dao.MantraTranslationChapterContributorDao
import press.mantra.compose.database.dao.MantraTranslationChunkDao
import press.mantra.compose.database.dao.MantraTranslationDao
import press.mantra.compose.database.dao.MantraTranslationTranslatorDao
import press.mantra.compose.database.dao.MantraTranslationContributorDao
import press.mantra.compose.database.dao.MarmotCommitResultDao
import press.mantra.compose.database.dao.MarmotOutboundDao
import press.mantra.compose.database.dao.MarmotGroupEventDao
@@ -76,12 +75,11 @@ import press.mantra.compose.database.model.MantraChunk
import press.mantra.compose.database.model.MantraDialect
import press.mantra.compose.database.model.MantraTranslation
import press.mantra.compose.database.model.MantraTranslationArtifactVersion
import press.mantra.compose.database.model.MantraTranslationArtifactVersionEditor
import press.mantra.compose.database.model.MantraTranslationArtifactVersionContributor
import press.mantra.compose.database.model.MantraTranslationChapter
import press.mantra.compose.database.model.MantraTranslationChapterProofReader
import press.mantra.compose.database.model.MantraTranslationChapterTranslator
import press.mantra.compose.database.model.MantraTranslationChapterContributor
import press.mantra.compose.database.model.MantraTranslationChunk
import press.mantra.compose.database.model.MantraTranslationTranslator
import press.mantra.compose.database.model.MantraTranslationContributor
import press.mantra.compose.database.model.MarmotCommitResult
import press.mantra.compose.database.model.MarmotGroupEvent
import press.mantra.compose.database.model.MarmotInnerEvent
@@ -130,12 +128,11 @@ val GENESIS_AT = Instant.fromEpochMilliseconds(1231006505000L)
MantraDialect::class,
MantraTranslation::class,
MantraTranslationArtifactVersion::class,
MantraTranslationArtifactVersionEditor::class,
MantraTranslationArtifactVersionContributor::class,
MantraTranslationChapter::class,
MantraTranslationChapterProofReader::class,
MantraTranslationChapterTranslator::class,
MantraTranslationChapterContributor::class,
MantraTranslationChunk::class,
MantraTranslationTranslator::class,
MantraTranslationContributor::class,
MarmotCommitResult::class,
MarmotGroupEvent::class,
MarmotInnerEvent::class,
@@ -199,19 +196,17 @@ abstract class MantraDatabase: RoomDatabase() {
abstract fun mantraTranslationArtifactVersionDao(): MantraTranslationArtifactVersionDao
abstract fun mantraTranslationArtifactVersionEditorDao(): MantraTranslationArtifactVersionEditorDao
abstract fun mantraTranslationArtifactVersionContributorDao(): MantraTranslationArtifactVersionContributorDao
abstract fun mantraTranslationChapterDao(): MantraTranslationChapterDao
abstract fun mantraTranslationChapterProofReaderDao(): MantraTranslationChapterProofReaderDao
abstract fun mantraTranslationChapterTranslatorDao(): MantraTranslationChapterTranslatorDao
abstract fun mantraTranslationChapterContributorDao(): MantraTranslationChapterContributorDao
abstract fun mantraTranslationChunkDao(): MantraTranslationChunkDao
abstract fun mantraTranslationDao(): MantraTranslationDao
abstract fun mantraTranslationTranslatorDao(): MantraTranslationTranslatorDao
abstract fun mantraTranslationContributorDao(): MantraTranslationContributorDao
abstract fun marmotCommitResultDao(): MarmotCommitResultDao

View File

@@ -0,0 +1,11 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationArtifactVersionContributor
@Dao
interface MantraTranslationArtifactVersionContributorDao {
@Upsert
suspend fun upsert(mantraTranslationArtifactVersionContributor: MantraTranslationArtifactVersionContributor)
}

View File

@@ -1,13 +0,0 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationArtifactVersionEditor
@Dao
interface MantraTranslationArtifactVersionEditorDao {
@Upsert
suspend fun upsert(mantraTranslationArtifactVersion: MantraTranslationArtifactVersionEditor)
}

View File

@@ -0,0 +1,12 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationChapter
import press.mantra.compose.database.model.MantraTranslationChapterContributor
@Dao
interface MantraTranslationChapterContributorDao {
@Upsert
suspend fun upsert(mantraTranslationChapterContributor: MantraTranslationChapterContributor)
}

View File

@@ -1,11 +0,0 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationChapterProofReader
@Dao
interface MantraTranslationChapterProofReaderDao {
@Upsert
suspend fun upsert(mantraTranslationChapterProofReader: MantraTranslationChapterProofReader)
}

View File

@@ -1,13 +0,0 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationChapter
@Dao
interface MantraTranslationChapterTranslatorDao {
@Upsert
suspend fun upsert(mantraTranslationChapter: MantraTranslationChapter)
}

View File

@@ -0,0 +1,11 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationContributor
@Dao
interface MantraTranslationContributorDao {
@Upsert
suspend fun upsert(mantraTranslationContributor: MantraTranslationContributor)
}

View File

@@ -1,13 +0,0 @@
package press.mantra.compose.database.dao
import androidx.room3.Dao
import androidx.room3.Insert
import androidx.room3.OnConflictStrategy.Companion.IGNORE
import androidx.room3.Upsert
import press.mantra.compose.database.model.MantraTranslationTranslator
@Dao
interface MantraTranslationTranslatorDao {
@Upsert
suspend fun upsert(mantraTranslationTranslator: MantraTranslationTranslator)
}

View File

@@ -4,12 +4,11 @@ import androidx.room3.Entity
import androidx.room3.PrimaryKey
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nipF4Podcasts.metadata.tags.AuthorTag
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.TranslationArtifactVersionEditorListEvent
import press.mantra.compose.nostr.nip30303.TranslationChapterProofReaderListEvent
import press.mantra.compose.nostr.nip30303.TranslationArtifactVersionContributorListEvent
import press.mantra.compose.nostr.nip30303.tags.ContributorTag
import press.mantra.compose.nostr.nip30303.tags.TranslationArtifactVersionIdTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -18,7 +17,7 @@ import kotlin.time.Instant
* 30307
*/
@Entity
data class MantraTranslationArtifactVersionEditor(
data class MantraTranslationArtifactVersionContributor(
@PrimaryKey
val id: HexKey,
val publicKey: HexKey,
@@ -42,19 +41,23 @@ data class MantraTranslationArtifactVersionEditor(
): OptionalNostrEventEntity, TimestampedEntity {
companion object {
fun toTranslationChapterProofReaderListEvent(mantraTranslationChapterProofReaders: List<MantraTranslationChapterProofReader>): TranslationArtifactVersionEditorListEvent {
fun toTranslationChapterProofReaderListEvent(mantraTranslationChapterProofReaders: List<MantraTranslationArtifactVersionContributor>): TranslationArtifactVersionContributorListEvent {
// TODO: create TranslationChapterProofReaderListEvent from mantraTranslationChapterProofReaders
return TranslationArtifactVersionEditorListEvent(
return TranslationArtifactVersionContributorListEvent(
id = "",
pubKey = "",
createdAt = TimeUtils.now(),
tags = TagArrayBuilder<TranslationChapterProofReaderListEvent>()
tags = TagArrayBuilder<TranslationArtifactVersionContributorListEvent>()
.addUnique(
TranslationArtifactVersionIdTag.assemble("") // TODO: use actual value..
)
// TODO: Add tag for editorPublicKey
.addUnique(
AuthorTag.assemble("") // TODO: Get piblicKeyList...
ContributorTag.assemble(
"",
ContributorTag.ROLE_EDITOR
)
)
.build(),
content = "",

View File

@@ -8,7 +8,8 @@ import com.vitorpamplona.quartz.nipF4Podcasts.metadata.tags.AuthorTag
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.TranslationChapterTranslatorListEvent
import press.mantra.compose.nostr.nip30303.TranslationChapterContributorListEvent
import press.mantra.compose.nostr.nip30303.tags.ContributorTag
import press.mantra.compose.nostr.nip30303.tags.TranslationChapterIdTag
import kotlin.time.Clock
import kotlin.time.Instant
@@ -17,13 +18,14 @@ import kotlin.time.Instant
* 30310
*/
@Entity
data class MantraTranslationChapterTranslator(
data class MantraTranslationChapterContributor(
@PrimaryKey
val id: HexKey,
val publicKey: HexKey,
val translationChapterId: HexKey,
val translatorPublicKey: HexKey,
val role: String,
/**
* Signature will be an empty string if this is a rumor
*/
@@ -41,18 +43,18 @@ data class MantraTranslationChapterTranslator(
): OptionalNostrEventEntity, TimestampedEntity {
companion object {
fun toTranslationChapterTranslatorListEvent(mantraTranslationChapterTranslators: List<MantraTranslationChapterTranslator>): TranslationChapterTranslatorListEvent {
fun toTranslationChapterTranslatorListEvent(mantraTranslationChapterTranslators: List<MantraTranslationChapterTranslator>): TranslationChapterContributorListEvent {
// TODO: create TranslationChapterProofReaderListEvent from mantraTranslationChapterProofReaders
return TranslationChapterTranslatorListEvent(
return TranslationChapterContributorListEvent(
id = "",
pubKey = "",
createdAt = TimeUtils.now(),
tags = TagArrayBuilder<TranslationChapterTranslatorListEvent>()
tags = TagArrayBuilder<TranslationChapterContributorListEvent>()
.addUnique(
TranslationChapterIdTag.assemble("")
)
.addUnique(
AuthorTag.assemble("") // TODO: PublicKeyListTag
ContributorTag.assemble("", ContributorTag.ROLE_TRANSLATOR)
)
.build(),
content = "",

View File

@@ -1,64 +0,0 @@
package press.mantra.compose.database.model
import androidx.room3.Entity
import androidx.room3.PrimaryKey
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nipF4Podcasts.metadata.tags.AuthorTag
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.TranslationChapterProofReaderListEvent
import press.mantra.compose.nostr.nip30303.tags.TranslationChapterIdTag
import kotlin.time.Clock
import kotlin.time.Instant
/**
* 30309
*/
@Entity
data class MantraTranslationChapterProofReader(
@PrimaryKey
val id: HexKey,
val publicKey: HexKey,
val translationChapterId: HexKey,
val proofReaderPublicKey: HexKey,
/**
* Signature will be an empty string if this is a rumor
*/
val signature: String,
val relayHint: String? = null,
val marmotGroupEventId: HexKey? = null,
/**
* Related nostrEventId if this is not a rumor...
*/
override val nostrEventId: HexKey? = null,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = createdAt
): OptionalNostrEventEntity, TimestampedEntity {
companion object {
fun toTranslationChapterProofReaderListEvent(mantraTranslationChapterProofReaders: List<MantraTranslationChapterProofReader>): TranslationChapterProofReaderListEvent {
// TODO: create TranslationChapterProofReaderListEvent from mantraTranslationChapterProofReaders
return TranslationChapterProofReaderListEvent(
id = "",
pubKey = "",
createdAt = TimeUtils.now(),
tags = TagArrayBuilder<TranslationChapterProofReaderListEvent>()
.addUnique(
TranslationChapterIdTag.assemble("")
)
.addUnique(
AuthorTag.assemble("") // TODO: PublicKeyListTag
)
.build(),
content = "",
sig = ""
)
}
}
}

View File

@@ -0,0 +1,95 @@
package press.mantra.compose.database.model
import androidx.room3.Entity
import androidx.room3.PrimaryKey
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.crypto.EventHasher
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip01Core.tags.dTag.DTag
import com.vitorpamplona.quartz.nip31Alts.alt
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.TranslationContributorListEvent
import press.mantra.compose.nostr.nip30303.tags.ContributorTag
import kotlin.time.Clock
import kotlin.time.Instant
/**
* 30312
*/
@Entity
data class MantraTranslationContributor(
@PrimaryKey
val id: HexKey,
val dTag: String,
val publicKey: HexKey,
val translationId: HexKey,
val translatorPublicKey: HexKey,
/**
* Signature will be an empty string if this is a rumor
*/
val signature: String,
val relayHint: String? = null,
val marmotGroupEventId: HexKey? = null,
/**
* Related nostrEventId if this is not a rumor...
*/
override val nostrEventId: HexKey? = null,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = createdAt
): OptionalNostrEventEntity, TimestampedEntity {
companion object {
fun toTranslationChapterProofReaderListEvent(mantraTranslationContributors: List<MantraTranslationContributor>): TranslationContributorListEvent? {
mantraTranslationContributors.firstOrNull()?.let { firstMantraTranslationTranslator ->
val allFromSameEvent = mantraTranslationContributors.all {
it.publicKey == firstMantraTranslationTranslator.publicKey
&& it.createdAt == firstMantraTranslationTranslator.createdAt
&& it.dTag == firstMantraTranslationTranslator.dTag
}
if (allFromSameEvent) {
val translatorPublicKeys = mantraTranslationContributors.map {
ContributorTag.assemble(
pubKey = it.publicKey,
role = ContributorTag.ROLE_TRANSLATOR
)
}
val template = eventTemplate<TranslationContributorListEvent>(
kind = TranslationContributorListEvent.KIND,
description = "",
createdAt = firstMantraTranslationTranslator.createdAt.epochSeconds
) {
addUnique(
DTag.assemble(
firstMantraTranslationTranslator.dTag
)
)
alt(TranslationContributorListEvent.ALT_DESCRIPTION)
}
return TranslationContributorListEvent(
id = EventHasher.hashId(
pubKey = firstMantraTranslationTranslator.publicKey,
tags = template.tags,
createdAt = template.createdAt,
content = template.content,
kind = template.kind
),
pubKey = firstMantraTranslationTranslator.publicKey,
createdAt = firstMantraTranslationTranslator.createdAt.epochSeconds,
tags = translatorPublicKeys.toTypedArray(),
content = template.content,
sig = firstMantraTranslationTranslator.signature
)
}
}
return null
}
}
}

View File

@@ -1,64 +0,0 @@
package press.mantra.compose.database.model
import androidx.room3.Entity
import androidx.room3.PrimaryKey
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nipF4Podcasts.metadata.tags.AuthorTag
import com.vitorpamplona.quartz.utils.TimeUtils
import press.mantra.compose.database.model.traits.OptionalNostrEventEntity
import press.mantra.compose.database.model.traits.TimestampedEntity
import press.mantra.compose.nostr.nip30303.TranslationTranslatorListEvent
import press.mantra.compose.nostr.nip30303.tags.TranslationIdTag
import kotlin.time.Clock
import kotlin.time.Instant
/**
* 30312
*/
@Entity
data class MantraTranslationTranslator(
@PrimaryKey
val id: HexKey,
val publicKey: HexKey,
val translationId: HexKey,
val translatorPublicKey: HexKey,
/**
* Signature will be an empty string if this is a rumor
*/
val signature: String,
val relayHint: String? = null,
val marmotGroupEventId: HexKey? = null,
/**
* Related nostrEventId if this is not a rumor...
*/
override val nostrEventId: HexKey? = null,
override val createdAt: Instant = Clock.System.now(),
override val updatedAt: Instant = createdAt
): OptionalNostrEventEntity, TimestampedEntity {
companion object {
fun toTranslationChapterProofReaderListEvent(mantraTranslationChapterProofReaders: List<MantraTranslationChapterProofReader>): TranslationTranslatorListEvent {
// TODO: create TranslationChapterProofReaderListEvent from mantraTranslationChapterProofReaders
return TranslationTranslatorListEvent(
id = "",
pubKey = "",
createdAt = TimeUtils.now(),
tags = TagArrayBuilder<TranslationTranslatorListEvent>()
.addUnique(
TranslationIdTag.assemble("") // TODO: Use actual translationId
)
.addUnique(
AuthorTag.assemble("") // TODO: PublicKeyListTag
)
.build(),
content = "",
sig = ""
)
}
}
}

View File

@@ -12,7 +12,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
class TranslationArtifactVersionEditorListEvent(
class TranslationArtifactVersionContributorListEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
@@ -23,7 +23,7 @@ class TranslationArtifactVersionEditorListEvent(
companion object {
const val KIND = 30305
const val ALT_DESCRIPTION = "TranslationArtifactVersionEditorList"
const val ALT_DESCRIPTION = "TranslationArtifactVersionContributorListEvent"
fun build(
content: String,

View File

@@ -12,7 +12,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
class TranslationChapterProofReaderListEvent(
class TranslationChapterContributorListEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
@@ -22,8 +22,8 @@ class TranslationChapterProofReaderListEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 30308
const val ALT_DESCRIPTION = "TranslationChapterProofReaderList"
const val KIND = 30307
const val ALT_DESCRIPTION = "TranslationChapterContributorListEvent"
fun build(
content: String,

View File

@@ -22,7 +22,7 @@ class TranslationChapterEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 30307
const val KIND = 30308
const val ALT_DESCRIPTION = "TranslationChapter"
fun build(

View File

@@ -1,37 +0,0 @@
package press.mantra.compose.nostr.nip30303
import androidx.compose.runtime.Immutable
import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.TagArrayBuilder
import com.vitorpamplona.quartz.nip01Core.signers.eventTemplate
import com.vitorpamplona.quartz.nip22Comments.RootScope
import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip50Search.SearchableEvent
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
class TranslationChapterTranslatorListEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
tags: Array<Array<String>>,
content: String,
sig: HexKey,
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 30309
const val ALT_DESCRIPTION = "TranslationChapterTranslatorList"
fun build(
content: String,
createdAt: Long = TimeUtils.now(),
initializer: TagArrayBuilder<ArtifactEvent>.() -> Unit = {},
) = eventTemplate(KIND, content, createdAt) {
alt(ALT_DESCRIPTION)
initializer()
}
}
}

View File

@@ -22,7 +22,7 @@ class TranslationChunkEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 30310
const val KIND = 30309
const val ALT_DESCRIPTION = "TranslationChunk"
fun build(

View File

@@ -12,7 +12,7 @@ import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
class TranslationTranslatorListEvent(
class TranslationContributorListEvent(
id: HexKey,
pubKey: HexKey,
createdAt: Long,
@@ -22,8 +22,8 @@ class TranslationTranslatorListEvent(
) : Event(id, pubKey, createdAt, KIND, tags, content, sig) {
companion object {
const val KIND = 30312
const val ALT_DESCRIPTION = "TranslationTranslatorList"
const val KIND = 30310
const val ALT_DESCRIPTION = "TranslationContributorListEvent"
fun build(
content: String,

View File

@@ -0,0 +1,52 @@
package press.mantra.compose.nostr.nip30303.tags
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.ensure
/**
* Based on NIP-F4
*/
class ContributorTag(
val pubKey: HexKey,
val role: String? = null,
) {
fun toTagArray() = assemble(pubKey, role)
companion object {
const val TAG_NAME = "p"
const val ROLE_TRANSLATOR = "translator"
const val ROLE_PROOF_READER = "proofreader"
const val ROLE_EDITOR = "editor"
private val KNOWN_ROLES = setOf(ROLE_TRANSLATOR, ROLE_PROOF_READER, ROLE_EDITOR)
fun isTagged(tag: Array<String>) = tag.has(1) && tag[0] == TAG_NAME && tag[1].length == 64
fun parse(tag: Array<String>): ContributorTag? {
ensure(tag.has(1)) { return null }
ensure(tag[0] == TAG_NAME) { return null }
ensure(tag[1].length == 64) { return null }
// NIP-F4 overloads slot 2 as role — but only host/cohost/editor are spec-defined.
// Anything else (notably a stale relay-hint URL copied from a kind:3 p-tag) gets
// dropped so it doesn't render as 'Role: wss://relay…' under the author chip.
val role = tag.getOrNull(2)?.takeIf { it in KNOWN_ROLES }
return ContributorTag(tag[1], role)
}
fun assemble(
pubKey: HexKey,
role: String? = null,
): Array<String> =
if (role.isNullOrEmpty()) {
arrayOf(TAG_NAME, pubKey)
} else {
arrayOf(TAG_NAME, pubKey, role)
}
fun assemble(author: ContributorTag) = assemble(author.pubKey, author.role)
fun assemble(authors: List<ContributorTag>) = authors.map { assemble(it) }
}
}