Files

74 lines
1.9 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package tentaclewire
import (
"bytes"
"encoding/json"
"unicode"
"unicode/utf8"
)
const MaxPresentationBytes = 8192 // Per channel, measured in UTF-8 bytes.
// Presentation is a command result, never an unsolicited announcement. Empty
// channels are omitted; neither channel supplies positions or routing targets.
type Presentation struct {
Speech string `json:"speech,omitempty"`
Braille string `json:"braille,omitempty"`
Uninterruptible bool `json:"uninterruptible,omitempty"`
}
func DecodePresentation(data []byte) (Presentation, error) {
var value Presentation
if err := DecodeStrict(data, &value); err != nil {
return value, err
}
var fields map[string]json.RawMessage
if json.Unmarshal(data, &fields) != nil || fields == nil || len(fields) == 0 {
return value, ErrProtocol
}
for _, field := range fields {
if bytes.Equal(field, []byte("null")) {
return value, ErrProtocol
}
}
if value.Speech == "" && value.Braille == "" {
return value, ErrProtocol
}
if value.Uninterruptible && value.Speech == "" {
return value, ErrProtocol
}
for _, text := range []string{value.Speech, value.Braille} {
if len(text) > MaxPresentationBytes {
return value, ErrLimit
}
if !utf8.ValidString(text) {
return value, ErrProtocol
}
for _, char := range text {
if unicode.IsControl(char) && char != '\n' && char != '\t' {
return value, ErrProtocol
}
}
}
return value, nil
}
// The experimental optional API is additive to the command-only handshake.
func ValidAPIs(apis, capabilities []string) bool {
if len(apis) != len(capabilities) || len(apis) < 1 || len(apis) > 3 || apis[0] != "commands" {
return false
}
if len(apis) >= 2 && apis[1] != "presentation" || len(apis) == 3 && apis[2] != "interaction" {
return false
}
for index := range apis {
if apis[index] != capabilities[index] {
return false
}
}
return true
}