Wrote roots-ws golang implementation.

This commit is contained in:
Jay
2025-11-02 14:31:46 -05:00
parent 53c42911b4
commit 82e58a193d
13 changed files with 1972 additions and 0 deletions

324
envelope/find.go Normal file
View File

@@ -0,0 +1,324 @@
package envelope
import (
"encoding/json"
"fmt"
"git.wisehodl.dev/jay/go-roots-ws/errors"
)
// CheckArrayLength is a helper function that ensures the JSON array has at
// least the minimum length required
func CheckArrayLength(arr []json.RawMessage, minLen int) error {
if len(arr) < minLen {
return fmt.Errorf("%w: expected %d elements, got %d", errors.InvalidEnvelope, minLen, len(arr))
}
return nil
}
// CheckLabel is a helper function that verifies that the envelope label
// matches the expected one
func CheckLabel(got, want string) error {
if got != want {
return fmt.Errorf("%w: expected %s, got %s", errors.WrongEnvelopeLabel, want, got)
}
return nil
}
// ParseElement is a helper function that unmarshals an array element into the
// provided value
func ParseElement(element json.RawMessage, value interface{}, position string) error {
if err := json.Unmarshal(element, value); err != nil {
return fmt.Errorf("%w: %s is not the expected type", errors.WrongFieldType, position)
}
return nil
}
// FindEvent extracts an event from an EVENT envelope with no subscription ID.
// Expected Format: ["EVENT", event]
func FindEvent(env Envelope) ([]byte, error) {
var arr []json.RawMessage
if err := json.Unmarshal(env, &arr); err != nil {
return nil, fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err := CheckArrayLength(arr, 2); err != nil {
return nil, err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return nil, err
}
if err := CheckLabel(label, "EVENT"); err != nil {
return nil, err
}
return arr[1], nil
}
// FindSubscriptionEvent extracts an event and subscription ID from an EVENT envelope.
// Expected Format: ["EVENT", subID, event]
func FindSubscriptionEvent(env Envelope) (subID string, event []byte, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", nil, fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 3); err != nil {
return "", nil, err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", nil, err
}
if err = CheckLabel(label, "EVENT"); err != nil {
return "", nil, err
}
if err = ParseElement(arr[1], &subID, "subscription ID"); err != nil {
return "", nil, err
}
return subID, arr[2], nil
}
// FindOK extracts eventID, status, and message from an OK envelope.
// Expected Format: ["OK", eventID, status, message]
func FindOK(env Envelope) (eventID string, status bool, message string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", false, "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 4); err != nil {
return "", false, "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", false, "", err
}
if err = CheckLabel(label, "OK"); err != nil {
return "", false, "", err
}
if err = ParseElement(arr[1], &eventID, "event ID"); err != nil {
return "", false, "", err
}
if err = ParseElement(arr[2], &status, "status"); err != nil {
return "", false, "", err
}
if err = ParseElement(arr[3], &message, "message"); err != nil {
return "", false, "", err
}
return eventID, status, message, nil
}
// FindReq extracts subscription ID and filters from a REQ envelope.
// Expected Format: ["REQ", subID, filter1, filter2, ...]
func FindReq(env Envelope) (subID string, filters [][]byte, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", nil, fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return "", nil, err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", nil, err
}
if err = CheckLabel(label, "REQ"); err != nil {
return "", nil, err
}
if err = ParseElement(arr[1], &subID, "subscription ID"); err != nil {
return "", nil, err
}
filters = make([][]byte, 0, len(arr)-2)
for i := 2; i < len(arr); i++ {
filters = append(filters, arr[i])
}
return subID, filters, nil
}
// FindEOSE extracts subscription ID from an EOSE envelope.
// Expected Format: ["EOSE", subID]
func FindEOSE(env Envelope) (subID string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", err
}
if err = CheckLabel(label, "EOSE"); err != nil {
return "", err
}
if err = ParseElement(arr[1], &subID, "subscription ID"); err != nil {
return "", err
}
return subID, nil
}
// FindClose extracts subscription ID from a CLOSE envelope.
// Expected Format: ["CLOSE", subID]
func FindClose(env Envelope) (subID string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", err
}
if err = CheckLabel(label, "CLOSE"); err != nil {
return "", err
}
if err = ParseElement(arr[1], &subID, "subscription ID"); err != nil {
return "", err
}
return subID, nil
}
// FindClosed extracts subscription ID and message from a CLOSED envelope.
// Expected Format: ["CLOSED", subID, message]
func FindClosed(env Envelope) (subID string, message string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 3); err != nil {
return "", "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", "", err
}
if err = CheckLabel(label, "CLOSED"); err != nil {
return "", "", err
}
if err = ParseElement(arr[1], &subID, "subscription ID"); err != nil {
return "", "", err
}
if err = ParseElement(arr[2], &message, "message"); err != nil {
return "", "", err
}
return subID, message, nil
}
// FindNotice extracts message from a NOTICE envelope.
// Expected Format: ["NOTICE", message]
func FindNotice(env Envelope) (message string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", err
}
if err = CheckLabel(label, "NOTICE"); err != nil {
return "", err
}
if err = ParseElement(arr[1], &message, "message"); err != nil {
return "", err
}
return message, nil
}
// FindAuthChallenge extracts challenge from an AUTH challenge envelope.
// Expected Format: ["AUTH", challenge]
func FindAuthChallenge(env Envelope) (challenge string, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return "", fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return "", err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return "", err
}
if err = CheckLabel(label, "AUTH"); err != nil {
return "", err
}
// Check if the second element is a string (AUTH challenge)
if err = ParseElement(arr[1], &challenge, "challenge"); err != nil {
return "", err
}
return challenge, nil
}
// FindAuthResponse extracts event from an AUTH response envelope.
// Expected Format: ["AUTH", event]
func FindAuthResponse(env Envelope) (event []byte, err error) {
var arr []json.RawMessage
if err = json.Unmarshal(env, &arr); err != nil {
return nil, fmt.Errorf("%w: %v", errors.InvalidJSON, err)
}
if err = CheckArrayLength(arr, 2); err != nil {
return nil, err
}
var label string
if err := ParseElement(arr[0], &label, "envelope label"); err != nil {
return nil, err
}
if err = CheckLabel(label, "AUTH"); err != nil {
return nil, err
}
return arr[1], nil
}