mirror of https://github.com/waku-org/nwaku.git
feat(message): differentiate content and pubsub topic namespacing
This commit is contained in:
parent
d5f93e385d
commit
67db35e29d
|
@ -20,7 +20,7 @@ proc toV1Topic*(contentTopic: ContentTopic): waku_protocol.Topic {.raises: [Valu
|
||||||
## Extracts the 4-byte array v1 topic from a content topic
|
## Extracts the 4-byte array v1 topic from a content topic
|
||||||
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
||||||
|
|
||||||
let ns = NamespacedTopic.parse(contentTopic)
|
let ns = NsContentTopic.parse(contentTopic)
|
||||||
if ns.isErr():
|
if ns.isErr():
|
||||||
let err = ns.tryError()
|
let err = ns.tryError()
|
||||||
raise newException(ValueError, $err)
|
raise newException(ValueError, $err)
|
||||||
|
@ -33,11 +33,11 @@ proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
|
||||||
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
## with format `/waku/1/<v1-topic-bytes-as-hex>/rfc26`
|
||||||
##
|
##
|
||||||
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
|
## <v1-topic-bytes-as-hex> should be prefixed with `0x`
|
||||||
var namespacedTopic = NamespacedTopic()
|
var namespacedTopic = NsContentTopic()
|
||||||
|
|
||||||
namespacedTopic.application = ContentTopicApplication
|
namespacedTopic.application = ContentTopicApplication
|
||||||
namespacedTopic.version = ContentTopicAppVersion
|
namespacedTopic.version = ContentTopicAppVersion
|
||||||
namespacedTopic.name = "0x" & v1Topic.toHex()
|
namespacedTopic.name = v1Topic.to0xHex()
|
||||||
namespacedTopic.encoding = "rfc26"
|
namespacedTopic.encoding = "rfc26"
|
||||||
|
|
||||||
return ContentTopic($namespacedTopic)
|
return ContentTopic($namespacedTopic)
|
||||||
|
@ -45,7 +45,7 @@ proc toV2ContentTopic*(v1Topic: waku_protocol.Topic): ContentTopic =
|
||||||
|
|
||||||
proc isBridgeable*(msg: WakuMessage): bool =
|
proc isBridgeable*(msg: WakuMessage): bool =
|
||||||
## Determines if a Waku v2 msg is on a bridgeable content topic
|
## Determines if a Waku v2 msg is on a bridgeable content topic
|
||||||
let ns = NamespacedTopic.parse(msg.contentTopic)
|
let ns = NsContentTopic.parse(msg.contentTopic)
|
||||||
if ns.isErr():
|
if ns.isErr():
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import
|
||||||
import
|
import
|
||||||
../../waku/v2/protocol/waku_message/topics
|
../../waku/v2/protocol/waku_message/topics
|
||||||
|
|
||||||
suite "Waku Message - Topics namespacing":
|
suite "Waku Message - Content topics namespacing":
|
||||||
|
|
||||||
test "Stringify namespaced topic":
|
test "Stringify namespaced content topic":
|
||||||
## Given
|
## Given
|
||||||
var ns = NamespacedTopic()
|
var ns = NsContentTopic()
|
||||||
ns.application = "waku"
|
ns.application = "toychat"
|
||||||
ns.version = "2"
|
ns.version = "2"
|
||||||
ns.name = "default-waku"
|
ns.name = "huilong"
|
||||||
ns.encoding = "proto"
|
ns.encoding = "proto"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
|
@ -21,79 +21,173 @@ suite "Waku Message - Topics namespacing":
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check:
|
check:
|
||||||
topic == "/waku/2/default-waku/proto"
|
topic == "/toychat/2/huilong/proto"
|
||||||
|
|
||||||
test "Parse topic string - Valid string":
|
test "Parse content topic string - Valid string":
|
||||||
## Given
|
## Given
|
||||||
let topic = "/waku/2/default-waku/proto"
|
let topic = "/toychat/2/huilong/proto"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
let nsRes = NamespacedTopic.parse(topic)
|
let nsRes = NsContentTopic.parse(topic)
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check nsRes.isOk()
|
check nsRes.isOk()
|
||||||
|
|
||||||
let ns = nsRes.get()
|
let ns = nsRes.get()
|
||||||
check:
|
check:
|
||||||
ns.application == "waku"
|
ns.application == "toychat"
|
||||||
ns.version == "2"
|
ns.version == "2"
|
||||||
ns.name == "default-waku"
|
ns.name == "huilong"
|
||||||
ns.encoding == "proto"
|
ns.encoding == "proto"
|
||||||
|
|
||||||
test "Parse topic string - Invalid string: doesn't start with slash":
|
test "Parse content topic string - Invalid string: missing leading slash":
|
||||||
## Given
|
## Given
|
||||||
let topic = "waku/2/default-waku/proto"
|
let topic = "toychat/2/huilong/proto"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
let ns = NamespacedTopic.parse(topic)
|
let ns = NsContentTopic.parse(topic)
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check ns.isErr()
|
check ns.isErr()
|
||||||
let err = ns.tryError()
|
let err = ns.tryError()
|
||||||
check:
|
check:
|
||||||
err.kind == NamespacingErrorKind.InvalidFormat
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
err.cause == "topic must start with slash"
|
err.cause == "topic must start with slash"
|
||||||
|
|
||||||
test "Parse topic string - Invalid string: not namespaced":
|
test "Parse content topic string - Invalid string: not namespaced":
|
||||||
## Given
|
## Given
|
||||||
let topic = "/this-is-not-namespaced"
|
let topic = "/this-is-not-namespaced"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
let ns = NamespacedTopic.parse(topic)
|
let ns = NsContentTopic.parse(topic)
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check ns.isErr()
|
check ns.isErr()
|
||||||
let err = ns.tryError()
|
let err = ns.tryError()
|
||||||
check:
|
check:
|
||||||
err.kind == NamespacingErrorKind.InvalidFormat
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
err.cause == "invalid topic structure"
|
err.cause == "invalid topic structure"
|
||||||
|
|
||||||
|
|
||||||
test "Parse topic string - Invalid string: missing encoding part":
|
test "Parse content topic string - Invalid string: missing encoding part":
|
||||||
## Given
|
## Given
|
||||||
let topic = "/waku/2/default-waku"
|
let topic = "/toychat/2/huilong"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
let ns = NamespacedTopic.parse(topic)
|
let ns = NsContentTopic.parse(topic)
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check ns.isErr()
|
check ns.isErr()
|
||||||
let err = ns.tryError()
|
let err = ns.tryError()
|
||||||
check:
|
check:
|
||||||
err.kind == NamespacingErrorKind.InvalidFormat
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
err.cause == "invalid topic structure"
|
err.cause == "invalid topic structure"
|
||||||
|
|
||||||
test "Parse topic string - Invalid string: too many parts":
|
test "Parse content topic string - Invalid string: too many parts":
|
||||||
## Given
|
## Given
|
||||||
let topic = "/waku/2/default-waku/proto/33"
|
let topic = "/toychat/2/huilong/proto/33"
|
||||||
|
|
||||||
## When
|
## When
|
||||||
let ns = NamespacedTopic.parse(topic)
|
let ns = NsContentTopic.parse(topic)
|
||||||
|
|
||||||
## Then
|
## Then
|
||||||
check ns.isErr()
|
check ns.isErr()
|
||||||
let err = ns.tryError()
|
let err = ns.tryError()
|
||||||
check:
|
check:
|
||||||
err.kind == NamespacingErrorKind.InvalidFormat
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
err.cause == "invalid topic structure"
|
err.cause == "invalid topic structure"
|
||||||
|
|
||||||
|
|
||||||
|
suite "Waku Message - Pub-sub topics namespacing":
|
||||||
|
|
||||||
|
test "Stringify named sharding pub-sub topic":
|
||||||
|
## Given
|
||||||
|
var ns = NsPubsubTopic.named("waku-dev")
|
||||||
|
|
||||||
|
## When
|
||||||
|
let topic = $ns
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check:
|
||||||
|
topic == "/waku/2/waku-dev"
|
||||||
|
|
||||||
|
test "Stringify static sharding pub-sub topic":
|
||||||
|
## Given
|
||||||
|
var ns = NsPubsubTopic.staticSharding(cluster=0, shard=2)
|
||||||
|
|
||||||
|
## When
|
||||||
|
let topic = $ns
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check:
|
||||||
|
topic == "/waku/2/rs/0/2"
|
||||||
|
|
||||||
|
test "Parse named pub-sub topic string - Valid string":
|
||||||
|
## Given
|
||||||
|
let topic = "/waku/2/waku-dev"
|
||||||
|
|
||||||
|
## When
|
||||||
|
let nsRes = NsPubsubTopic.parse(topic)
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check nsRes.isOk()
|
||||||
|
|
||||||
|
let ns = nsRes.get()
|
||||||
|
check:
|
||||||
|
ns.name == "waku-dev"
|
||||||
|
|
||||||
|
test "Parse static sharding pub-sub topic string - Valid string":
|
||||||
|
## Given
|
||||||
|
let topic = "/waku/2/rs/16/42"
|
||||||
|
|
||||||
|
## When
|
||||||
|
let nsRes = NsPubsubTopic.parse(topic)
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check nsRes.isOk()
|
||||||
|
|
||||||
|
let ns = nsRes.get()
|
||||||
|
check:
|
||||||
|
ns.cluster == 16
|
||||||
|
ns.shard == 42
|
||||||
|
|
||||||
|
test "Parse pub-sub topic string - Invalid string: invalid protocol version":
|
||||||
|
## Given
|
||||||
|
let topic = "/waku/1/rs/16/42"
|
||||||
|
|
||||||
|
## When
|
||||||
|
let ns = NsPubsubTopic.parse(topic)
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check ns.isErr()
|
||||||
|
let err = ns.tryError()
|
||||||
|
check:
|
||||||
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
|
|
||||||
|
test "Parse static sharding pub-sub topic string - Invalid string: empty shard value":
|
||||||
|
## Given
|
||||||
|
let topic = "/waku/2/rs//02"
|
||||||
|
|
||||||
|
## When
|
||||||
|
let ns = NsPubsubTopic.parse(topic)
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check ns.isErr()
|
||||||
|
let err = ns.tryError()
|
||||||
|
check:
|
||||||
|
err.kind == ParsingErrorKind.MissingPart
|
||||||
|
err.part == "shard_cluster_index"
|
||||||
|
|
||||||
|
|
||||||
|
test "Parse static sharding pub-sub topic string - Invalid string: cluster value":
|
||||||
|
## Given
|
||||||
|
let topic = "/waku/2/rs/xx/77"
|
||||||
|
|
||||||
|
## When
|
||||||
|
let ns = NsPubsubTopic.parse(topic)
|
||||||
|
|
||||||
|
## Then
|
||||||
|
check ns.isErr()
|
||||||
|
let err = ns.tryError()
|
||||||
|
check:
|
||||||
|
err.kind == ParsingErrorKind.InvalidFormat
|
||||||
|
|
|
@ -1,94 +1,7 @@
|
||||||
## Waku topics definition and namespacing utils
|
|
||||||
##
|
|
||||||
## See 14/WAKU2-MESSAGE RFC: https://rfc.vac.dev/spec/14/
|
|
||||||
## See 23/WAKU2-TOPICS RFC: https://rfc.vac.dev/spec/23/
|
|
||||||
|
|
||||||
when (NimMajor, NimMinor) < (1, 4):
|
|
||||||
{.push raises: [Defect].}
|
|
||||||
else:
|
|
||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
|
|
||||||
import
|
import
|
||||||
std/strutils,
|
./topics/content_topic,
|
||||||
stew/results
|
./topics/pubsub_topic
|
||||||
|
|
||||||
|
export
|
||||||
## Topics
|
content_topic,
|
||||||
|
pubsub_topic
|
||||||
type
|
|
||||||
PubsubTopic* = string
|
|
||||||
ContentTopic* = string
|
|
||||||
|
|
||||||
const
|
|
||||||
DefaultPubsubTopic* = PubsubTopic("/waku/2/default-waku/proto")
|
|
||||||
DefaultContentTopic* = ContentTopic("/waku/2/default-content/proto")
|
|
||||||
|
|
||||||
|
|
||||||
## Namespacing
|
|
||||||
|
|
||||||
type
|
|
||||||
NamespacedTopic* = object
|
|
||||||
application*: string
|
|
||||||
version*: string
|
|
||||||
name*: string
|
|
||||||
encoding*: string
|
|
||||||
|
|
||||||
type
|
|
||||||
NamespacingErrorKind* {.pure.} = enum
|
|
||||||
InvalidFormat
|
|
||||||
|
|
||||||
NamespacingError* = object
|
|
||||||
case kind*: NamespacingErrorKind
|
|
||||||
of InvalidFormat:
|
|
||||||
cause*: string
|
|
||||||
|
|
||||||
NamespacingResult*[T] = Result[T, NamespacingError]
|
|
||||||
|
|
||||||
|
|
||||||
proc invalidFormat(T: type NamespacingError, cause = "invalid format"): T =
|
|
||||||
NamespacingError(kind: NamespacingErrorKind.InvalidFormat, cause: cause)
|
|
||||||
|
|
||||||
proc `$`*(err: NamespacingError): string =
|
|
||||||
case err.kind:
|
|
||||||
of NamespacingErrorKind.InvalidFormat:
|
|
||||||
return "invalid format: " & err.cause
|
|
||||||
|
|
||||||
|
|
||||||
proc parse*(T: type NamespacedTopic, topic: PubsubTopic|ContentTopic|string): NamespacingResult[NamespacedTopic] =
|
|
||||||
## Splits a namespaced topic string into its constituent parts.
|
|
||||||
## The topic string has to be in the format `/<application>/<version>/<topic-name>/<encoding>`
|
|
||||||
|
|
||||||
if not topic.startsWith("/"):
|
|
||||||
return err(NamespacingError.invalidFormat("topic must start with slash"))
|
|
||||||
|
|
||||||
let parts = topic.split('/')
|
|
||||||
|
|
||||||
# Check that we have an expected number of substrings
|
|
||||||
if parts.len != 5:
|
|
||||||
return err(NamespacingError.invalidFormat("invalid topic structure"))
|
|
||||||
|
|
||||||
let namespaced = NamespacedTopic(
|
|
||||||
application: parts[1],
|
|
||||||
version: parts[2],
|
|
||||||
name: parts[3],
|
|
||||||
encoding: parts[4]
|
|
||||||
)
|
|
||||||
|
|
||||||
return ok(namespaced)
|
|
||||||
|
|
||||||
proc `$`*(namespacedTopic: NamespacedTopic): string =
|
|
||||||
## Returns a string representation of a namespaced topic
|
|
||||||
## in the format `/<application>/<version>/<topic-name>/<encoding>`
|
|
||||||
var str = newString(0)
|
|
||||||
|
|
||||||
str.add("/")
|
|
||||||
str.add(namespacedTopic.application)
|
|
||||||
str.add("/")
|
|
||||||
str.add(namespacedTopic.version)
|
|
||||||
str.add("/")
|
|
||||||
str.add(namespacedTopic.name)
|
|
||||||
str.add("/")
|
|
||||||
str.add(namespacedTopic.encoding)
|
|
||||||
|
|
||||||
return str
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
## Waku content topics definition and namespacing utils
|
||||||
|
##
|
||||||
|
## See 23/WAKU2-TOPICS RFC: https://rfc.vac.dev/spec/23/
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/strutils,
|
||||||
|
stew/results
|
||||||
|
import
|
||||||
|
./parsing
|
||||||
|
|
||||||
|
export parsing
|
||||||
|
|
||||||
|
|
||||||
|
## Content topic
|
||||||
|
|
||||||
|
type ContentTopic* = string
|
||||||
|
|
||||||
|
const DefaultContentTopic* = ContentTopic("/waku/2/default-content/proto")
|
||||||
|
|
||||||
|
|
||||||
|
## Namespaced content topic
|
||||||
|
|
||||||
|
type
|
||||||
|
NsContentTopic* = object
|
||||||
|
application*: string
|
||||||
|
version*: string
|
||||||
|
name*: string
|
||||||
|
encoding*: string
|
||||||
|
|
||||||
|
proc init*(T: type NsContentTopic, application, version, name, encoding: string): T =
|
||||||
|
NsContentTopic(
|
||||||
|
application: application,
|
||||||
|
version: version,
|
||||||
|
name: name,
|
||||||
|
encoding: encoding
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Serialization
|
||||||
|
|
||||||
|
proc `$`*(topic: NsContentTopic): string =
|
||||||
|
## Returns a string representation of a namespaced topic
|
||||||
|
## in the format `/<application>/<version>/<topic-name>/<encoding>`
|
||||||
|
"/" & topic.application & "/" & topic.version & "/" & topic.name & "/" & topic.encoding
|
||||||
|
|
||||||
|
|
||||||
|
# Deserialization
|
||||||
|
|
||||||
|
proc parse*(T: type NsContentTopic, topic: ContentTopic|string): ParsingResult[NsContentTopic] =
|
||||||
|
## Splits a namespaced topic string into its constituent parts.
|
||||||
|
## The topic string has to be in the format `/<application>/<version>/<topic-name>/<encoding>`
|
||||||
|
|
||||||
|
if not topic.startsWith("/"):
|
||||||
|
return err(ParsingError.invalidFormat("topic must start with slash"))
|
||||||
|
|
||||||
|
let parts = topic[1..<topic.len].split("/")
|
||||||
|
if parts.len != 4:
|
||||||
|
return err(ParsingError.invalidFormat("invalid topic structure"))
|
||||||
|
|
||||||
|
|
||||||
|
let app = parts[0]
|
||||||
|
if app.len == 0:
|
||||||
|
return err(ParsingError.missingPart("appplication"))
|
||||||
|
|
||||||
|
let ver = parts[1]
|
||||||
|
if ver.len == 0:
|
||||||
|
return err(ParsingError.missingPart("version"))
|
||||||
|
|
||||||
|
let name = parts[2]
|
||||||
|
if name.len == 0:
|
||||||
|
return err(ParsingError.missingPart("topic-name"))
|
||||||
|
|
||||||
|
let enc = parts[3]
|
||||||
|
if enc.len == 0:
|
||||||
|
return err(ParsingError.missingPart("encoding"))
|
||||||
|
|
||||||
|
|
||||||
|
ok(NsContentTopic.init(app, ver, name, enc))
|
||||||
|
|
||||||
|
|
||||||
|
# Content topic compatibility
|
||||||
|
|
||||||
|
converter toContentTopic*(topic: NsContentTopic): ContentTopic =
|
||||||
|
$topic
|
|
@ -0,0 +1,36 @@
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import stew/results
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
ParsingErrorKind* {.pure.} = enum
|
||||||
|
InvalidFormat,
|
||||||
|
MissingPart
|
||||||
|
|
||||||
|
ParsingError* = object
|
||||||
|
case kind*: ParsingErrorKind
|
||||||
|
of InvalidFormat:
|
||||||
|
cause*: string
|
||||||
|
of MissingPart:
|
||||||
|
part*: string
|
||||||
|
|
||||||
|
type ParsingResult*[T] = Result[T, ParsingError]
|
||||||
|
|
||||||
|
|
||||||
|
proc invalidFormat*(T: type ParsingError, cause = "invalid format"): T =
|
||||||
|
ParsingError(kind: ParsingErrorKind.InvalidFormat, cause: cause)
|
||||||
|
|
||||||
|
proc missingPart*(T: type ParsingError, part = "unknown"): T =
|
||||||
|
ParsingError(kind: ParsingErrorKind.MissingPart, part: part)
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(err: ParsingError): string =
|
||||||
|
case err.kind:
|
||||||
|
of ParsingErrorKind.InvalidFormat:
|
||||||
|
return "invalid format: " & err.cause
|
||||||
|
of ParsingErrorKind.MissingPart:
|
||||||
|
return "missing part: " & err.part
|
|
@ -0,0 +1,118 @@
|
||||||
|
## Waku pub-sub topics definition and namespacing utils
|
||||||
|
##
|
||||||
|
## See 23/WAKU2-TOPICS RFC: https://rfc.vac.dev/spec/23/
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) < (1, 4):
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
else:
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
|
||||||
|
import
|
||||||
|
std/strutils,
|
||||||
|
stew/[results, base10]
|
||||||
|
import
|
||||||
|
./parsing
|
||||||
|
|
||||||
|
export parsing
|
||||||
|
|
||||||
|
|
||||||
|
## Pub-sub topic
|
||||||
|
|
||||||
|
type PubsubTopic* = string
|
||||||
|
|
||||||
|
const DefaultPubsubTopic* = PubsubTopic("/waku/2/default-waku/proto")
|
||||||
|
|
||||||
|
|
||||||
|
## Namespaced pub-sub topic
|
||||||
|
|
||||||
|
type
|
||||||
|
NsPubsubTopicKind* {.pure.} = enum
|
||||||
|
StaticSharding,
|
||||||
|
NamedSharding
|
||||||
|
|
||||||
|
type
|
||||||
|
NsPubsubTopic* = object
|
||||||
|
case kind*: NsPubsubTopicKind
|
||||||
|
of NsPubsubTopicKind.StaticSharding:
|
||||||
|
cluster*: uint16
|
||||||
|
shard*: uint16
|
||||||
|
of NsPubsubTopicKind.NamedSharding:
|
||||||
|
name*: string
|
||||||
|
|
||||||
|
proc staticSharding*(T: type NsPubsubTopic, cluster, shard: uint16): T =
|
||||||
|
NsPubsubTopic(
|
||||||
|
kind: NsPubsubTopicKind.StaticSharding,
|
||||||
|
cluster: cluster,
|
||||||
|
shard: shard
|
||||||
|
)
|
||||||
|
|
||||||
|
proc named*(T: type NsPubsubTopic, name: string): T =
|
||||||
|
NsPubsubTopic(
|
||||||
|
kind: NsPubsubTopicKind.NamedSharding,
|
||||||
|
name: name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Serialization
|
||||||
|
|
||||||
|
proc `$`*(topic: NsPubsubTopic): string =
|
||||||
|
## Returns a string representation of a namespaced topic
|
||||||
|
## in the format `/waku/2/<raw-topic>
|
||||||
|
case topic.kind:
|
||||||
|
of NsPubsubTopicKind.NamedSharding:
|
||||||
|
"/waku/2/" & topic.name
|
||||||
|
of NsPubsubTopicKind.StaticSharding:
|
||||||
|
"/waku/2/rs/" & $topic.cluster & "/" & $topic.shard
|
||||||
|
|
||||||
|
|
||||||
|
# Deserialization
|
||||||
|
|
||||||
|
const
|
||||||
|
Waku2PubsubTopicPrefix = "/waku/2"
|
||||||
|
StaticShardingPubsubTopicPrefix = Waku2PubsubTopicPrefix & "/rs"
|
||||||
|
|
||||||
|
|
||||||
|
proc parseStaticSharding*(T: type NsPubsubTopic, topic: PubsubTopic|string): ParsingResult[NsPubsubTopic] =
|
||||||
|
if not topic.startsWith(StaticShardingPubsubTopicPrefix):
|
||||||
|
return err(ParsingError.invalidFormat("must start with " & StaticShardingPubsubTopicPrefix))
|
||||||
|
|
||||||
|
let parts = topic[11..<topic.len].split("/")
|
||||||
|
if parts.len != 2:
|
||||||
|
return err(ParsingError.invalidFormat("invalid topic structure"))
|
||||||
|
|
||||||
|
let clusterPart = parts[0]
|
||||||
|
if clusterPart.len == 0:
|
||||||
|
return err(ParsingError.missingPart("shard_cluster_index"))
|
||||||
|
let cluster = ?Base10.decode(uint16, clusterPart).mapErr(proc(err: auto): auto = ParsingError.invalidFormat($err))
|
||||||
|
|
||||||
|
let shardPart = parts[1]
|
||||||
|
if shardPart.len == 0:
|
||||||
|
return err(ParsingError.missingPart("shard_number"))
|
||||||
|
let shard = ?Base10.decode(uint16, shardPart).mapErr(proc(err: auto): auto = ParsingError.invalidFormat($err))
|
||||||
|
|
||||||
|
ok(NsPubsubTopic.staticSharding(cluster, shard))
|
||||||
|
|
||||||
|
proc parseNamedSharding*(T: type NsPubsubTopic, topic: PubsubTopic|string): ParsingResult[NsPubsubTopic] =
|
||||||
|
if not topic.startsWith(Waku2PubsubTopicPrefix):
|
||||||
|
return err(ParsingError.invalidFormat("must start with " & Waku2PubsubTopicPrefix))
|
||||||
|
|
||||||
|
let raw = topic[8..<topic.len]
|
||||||
|
if raw.len == 0:
|
||||||
|
return err(ParsingError.missingPart("topic-name"))
|
||||||
|
|
||||||
|
ok(NsPubsubTopic.named(name=raw))
|
||||||
|
|
||||||
|
proc parse*(T: type NsPubsubTopic, topic: PubsubTopic|string): ParsingResult[NsPubsubTopic] =
|
||||||
|
## Splits a namespaced topic string into its constituent parts.
|
||||||
|
## The topic string has to be in the format `/<application>/<version>/<topic-name>/<encoding>`
|
||||||
|
if topic.startsWith(StaticShardingPubsubTopicPrefix):
|
||||||
|
NsPubsubTopic.parseStaticSharding(topic)
|
||||||
|
else:
|
||||||
|
NsPubsubTopic.parseNamedSharding(topic)
|
||||||
|
|
||||||
|
|
||||||
|
# Pubsub topic compatibility
|
||||||
|
|
||||||
|
converter toPubsubTopic*(topic: NsPubsubTopic): PubsubTopic =
|
||||||
|
$topic
|
Loading…
Reference in New Issue