diff --git a/internal/processing/osc-message-create.go b/internal/processing/osc-message-create.go index 492b248..5f3869e 100644 --- a/internal/processing/osc-message-create.go +++ b/internal/processing/osc-message-create.go @@ -3,7 +3,9 @@ package processing import ( "bytes" "context" + "encoding/hex" "fmt" + "strconv" "text/template" "github.com/jwetzell/osc-go" @@ -12,6 +14,8 @@ import ( type OSCMessageCreate struct { config ProcessorConfig Address *template.Template + Args []*template.Template + Types string } func (o *OSCMessageCreate) Process(ctx context.Context, payload any) (any, error) { @@ -37,6 +41,31 @@ func (o *OSCMessageCreate) Process(ctx context.Context, payload any) (any, error Address: addressString, } + args := []osc.OSCArg{} + + for argIndex, argTemplate := range o.Args { + var argBuffer bytes.Buffer + err := argTemplate.Execute(&argBuffer, payload) + + if err != nil { + return nil, err + } + + argString := argBuffer.String() + + typedArg, err := argToTypedArg(argString, o.Types[argIndex]) + + if err != nil { + return nil, err + } + + args = append(args, typedArg) + } + + if len(args) > 0 { + payloadMessage.Args = args + } + return payloadMessage, nil } @@ -67,7 +96,123 @@ func init() { return nil, err } + args, ok := params["args"] + + if ok { + rawArgs, ok := args.([]interface{}) + + if !ok { + return nil, fmt.Errorf("osc.message.create address must be an array found %T", args) + } + + types, ok := params["types"] + + if !ok { + return nil, fmt.Errorf("osc.message.create requires a types parameter with args") + } + + typesString, ok := types.(string) + + if !ok { + return nil, fmt.Errorf("osc.message.create types must be a string") + } + + if len(rawArgs) != len(typesString) { + return nil, fmt.Errorf("osc.message.create args and types must be the same length") + } + + argTemplates := []*template.Template{} + + for _, rawArg := range rawArgs { + argString, ok := rawArg.(string) + + if !ok { + return nil, fmt.Errorf("osc.message.create arg must be a string") + } + + argTemplate, err := template.New("arg").Parse(argString) + + if err != nil { + return nil, err + } + argTemplates = append(argTemplates, argTemplate) + } + return &OSCMessageCreate{config: config, Address: addressTemplate, Args: argTemplates, Types: typesString}, nil + } return &OSCMessageCreate{config: config, Address: addressTemplate}, nil }, }) } + +func argToTypedArg(rawArg string, oscType byte) (osc.OSCArg, error) { + + switch oscType { + case 's': + return osc.OSCArg{ + Value: rawArg, + Type: "s", + }, nil + case 'i': + number, err := strconv.ParseInt(rawArg, 10, 32) + if err != nil { + return osc.OSCArg{}, err + } + return osc.OSCArg{ + Value: int32(number), + Type: "i", + }, nil + case 'f': + number, err := strconv.ParseFloat(rawArg, 32) + if err != nil { + return osc.OSCArg{}, err + } + return osc.OSCArg{ + Value: float32(number), + Type: "f", + }, nil + case 'b': + data, err := hex.DecodeString(rawArg) + if err != nil { + return osc.OSCArg{}, err + } + return osc.OSCArg{ + Value: data, + Type: "b", + }, nil + case 'h': + number, err := strconv.ParseInt(rawArg, 10, 64) + if err != nil { + return osc.OSCArg{}, err + } + return osc.OSCArg{ + Value: int64(number), + Type: "h", + }, nil + case 'd': + number, err := strconv.ParseFloat(rawArg, 64) + if err != nil { + return osc.OSCArg{}, err + } + return osc.OSCArg{ + Value: float64(number), + Type: "d", + }, nil + case 'T': + return osc.OSCArg{ + Value: true, + Type: "T", + }, nil + case 'F': + return osc.OSCArg{ + Value: false, + Type: "F", + }, nil + case 'N': + return osc.OSCArg{ + Value: nil, + Type: "N", + }, nil + default: + return osc.OSCArg{}, fmt.Errorf("osc.message.create unhandled osc type: %c", oscType) + } +}