beryl/topic
Pattern matching for topic routing.
Topics are string identifiers that clients join (e.g., "room:lobby").
Patterns define how topics are routed to the app's update function.
Patterns can be exact, legacy trailing prefix wildcards, or
segment-aware wildcards where "*" occupies a complete colon-delimited
segment.
ExtractError
Section titled “ExtractError”pub type ExtractError { NoWildcard TopicMismatch ExpectedOneWildcard(Int) EmptyNamespace}Errors from extracting wildcard values from a topic pattern.
Constructors
Section titled “Constructors”NoWildcard
Section titled “NoWildcard”NoWildcardThe pattern has no wildcard to extract.
TopicMismatch
Section titled “TopicMismatch”TopicMismatchThe topic does not match the pattern.
ExpectedOneWildcard
Section titled “ExpectedOneWildcard”ExpectedOneWildcard(Int)extract_id expected exactly one wildcard value but found this many.
EmptyNamespace
Section titled “EmptyNamespace”EmptyNamespacenamespace was called with an empty topic.
TopicError
Section titled “TopicError”pub type TopicError { EmptyTopic InvalidFormat(String)}Errors returned when validating a topic or topic pattern.
The variants are exhaustive; match each error explicitly. Adding a variant affects API compatibility and requires updating exhaustive matches.
Constructors
Section titled “Constructors”EmptyTopic
Section titled “EmptyTopic”EmptyTopicThe topic or pattern was an empty string.
InvalidFormat
Section titled “InvalidFormat”InvalidFormat(String)The topic, pattern, or event was malformed; the wrapped String
describes the problem (e.g. leading/trailing : or control characters).
TopicPattern
Section titled “TopicPattern”pub type TopicPattern { Exact(String) Wildcard(prefix: String) SegmentWildcard(segments: List(String))}A topic pattern for routing.
Constructors
Section titled “Constructors”Exact(String)Exact match. "room:lobby" matches only "room:lobby".
Wildcard
Section titled “Wildcard”Wildcard(prefix: String)Wildcard suffix. "room:*" matches "room:lobby", "room:123",
and other values with the same prefix.
SegmentWildcard
Section titled “SegmentWildcard”SegmentWildcard(segments: List(String))Segment wildcard. "document:*:ops" matches topics with the same
number of ":" segments. "*" occupies one complete segment.
Functions
Section titled “Functions”extract_id
Section titled “extract_id”pub fn extract_id( TopicPattern, String) -> Result(String, ExtractError)Extract the wildcard part of a topic.
Examples
Section titled “Examples”extract_id(Wildcard("room:"), "room:lobby") // -> Ok("lobby")extract_id(Wildcard("doc:"), "doc:abc:123") // -> Ok("abc:123")extract_id(SegmentWildcard(["doc", "*", "ops"]), "doc:abc:ops") // -> Ok("abc")extract_id(Exact("room:lobby"), "room:lobby") // -> Error(NoWildcard)extract_wildcards
Section titled “extract_wildcards”pub fn extract_wildcards( TopicPattern, String) -> Result(List(String), ExtractError)Extract values captured by wildcard segments.
For legacy prefix wildcards, this function returns the suffix as one
value. For segment wildcards, it returns each topic segment matched by
"*".
Examples
Section titled “Examples”extract_wildcards(parse_pattern("document:*:*"), "document:tenant-a:doc-42")// -> Ok(["tenant-a", "doc-42"])from_segments
Section titled “from_segments”pub fn from_segments(List(String)) -> StringBuild a topic from segments.
Examples
Section titled “Examples”from_segments(["room", "lobby"]) // -> "room:lobby"from_segments(["doc", "tenant", "123"]) // -> "doc:tenant:123"matches
Section titled “matches”pub fn matches( TopicPattern, String) -> BoolReturn whether a topic matches a pattern.
Examples
Section titled “Examples”matches(Wildcard("room:"), "room:lobby") // -> Truematches(Wildcard("room:"), "user:123") // -> Falsematches(Exact("room:lobby"), "room:lobby") // -> Truematches(Exact("room:lobby"), "room:other") // -> Falsematches(parse_pattern("document:*:ops"), "document:tenant-a:ops") // -> Truematches(parse_pattern("document:*:ops"), "document:tenant-a:view") // -> Falsenamespace
Section titled “namespace”pub fn namespace(String) -> Result(String, ExtractError)Return the first segment (namespace) of a topic.
Examples
Section titled “Examples”namespace("room:lobby") // -> Ok("room")namespace("") // -> Error(EmptyNamespace)parse_pattern
Section titled “parse_pattern”pub fn parse_pattern(String) -> TopicPatternParse a pattern string into TopicPattern.
Examples
Section titled “Examples”parse_pattern("room:*") // -> Wildcard("room:")parse_pattern("room:lobby") // -> Exact("room:lobby")parse_pattern("document:*:ops") // -> SegmentWildcard(["document", "*", "ops"])parse_pattern("document:*:*") // -> SegmentWildcard(["document", "*", "*"])parse_pattern("document:tenant-a:*") // -> Wildcard("document:tenant-a:")segments
Section titled “segments”pub fn segments(String) -> List(String)Split a topic into segments at each ":".
Examples
Section titled “Examples”segments("room:lobby") // -> ["room", "lobby"]segments("doc:tenant:123:ops") // -> ["doc", "tenant", "123", "ops"]validate
Section titled “validate”pub fn validate(String) -> Result(String, TopicError)Validate a topic string.
A topic must:
- not be empty;
- not contain control characters (codepoints 0–31 or 127); and
- not start or end with
":".
validate_event
Section titled “validate_event”pub fn validate_event(String) -> Result(String, TopicError)Validate an event name.
An event name must:
- not be empty; and
- not contain control characters (codepoints 0–31 or 127).
validate_pattern
Section titled “validate_pattern”pub fn validate_pattern(String) -> Result(String, TopicError)Validate a topic pattern string.
A pattern must:
- not be empty; and
- not contain control characters (codepoints 0–31 or 127).
The bare pattern "*" is valid: it parses to a catch-all wildcard that
matches every topic.
