Files
sanspie a46347e2c6
ci-platforms / darwin arm64 smoke (push) Skipped
ci / build (amd64, linux, ubuntu-24.04) (push) Failing after 12m19s
ci-platforms / darwin amd64 smoke (push) Skipped
ci-platforms / windows amd64 smoke (push) Skipped
github-native / gitea-pending (push) Skipped
github-native / darwin amd64 native (push) Skipped
github-native / linux amd64 native (push) Skipped
github-native / windows amd64 native (push) Skipped
github-native / darwin arm64 native (push) Skipped
github-native / gitea-result (push) Skipped
ci / checks (push) Failing after 23m57s
ci / release (push) Skipped
GitHub / windows amd64 native GitHub-hosted native CI: failure
GitHub / darwin arm64 native GitHub-hosted native CI: failure
GitHub / darwin amd64 native GitHub-hosted native CI: failure
GitHub / linux amd64 native GitHub-hosted native CI: success
editor: add art studio and stage 12.5 workflows
2026-11-08 18:47:57 +03:00

104 lines
2.5 KiB
Go

package art
import (
"bytes"
"encoding/json"
"fmt"
"io"
"strings"
)
const MaxOperationBytes = 2 << 20
// DecodeOperations is the bounded wire decoder used by agent/CLI transports.
// The algorithms and transaction state remain in the ordinary art core.
func DecodeOperations(data []byte) ([]Operation, error) {
if len(data) == 0 || len(data) > MaxOperationBytes {
return nil, ErrLimit
}
var ops []Operation
d := json.NewDecoder(bytes.NewReader(data))
d.DisallowUnknownFields()
if err := d.Decode(&ops); err != nil {
return nil, fmt.Errorf("art: invalid operations: %w", err)
}
if len(ops) == 0 || len(ops) > 4096 {
return nil, fmt.Errorf("art: proposal needs 1..4096 operations")
}
var trailing any
if err := d.Decode(&trailing); err != io.EOF {
return nil, fmt.Errorf("art: trailing operation data")
}
return ops, nil
}
// A batch can name a newly created entity with As and use "$name" in later
// entity fields. These are local result references, never external paths or
// selectors. No caller needs to predict the engine's stable entity IDs.
func resolveOperation(op Operation, names map[string]ID) (Operation, error) {
resolve := func(id *ID) error {
if !strings.HasPrefix(string(*id), "$") {
return nil
}
name := string(*id)[1:]
value, ok := names[name]
if !ok || value == "" {
return fmt.Errorf("art: unknown batch result %q", name)
}
*id = value
return nil
}
for _, id := range []*ID{&op.Cel, &op.Layer, &op.Frame, &op.Parent} {
if err := resolve(id); err != nil {
return op, err
}
}
op.FrameOrder = append([]ID(nil), op.FrameOrder...)
for i := range op.FrameOrder {
if err := resolve(&op.FrameOrder[i]); err != nil {
return op, err
}
}
op.Cells = append([]CellSlot(nil), op.Cells...)
for i := range op.Cells {
for _, id := range []*ID{&op.Cells[i].Layer, &op.Cells[i].Frame} {
if err := resolve(id); err != nil {
return op, err
}
}
}
if op.LayerValue != nil {
v := *op.LayerValue
op.LayerValue = &v
if err := resolve(&v.ID); err != nil {
return op, err
}
if err := resolve(&v.Parent); err != nil {
return op, err
}
}
if op.Tag != nil {
v := *op.Tag
op.Tag = &v
for _, id := range []*ID{&v.ID, &v.From, &v.To} {
if err := resolve(id); err != nil {
return op, err
}
}
}
return op, nil
}
func validResultName(name string) bool {
if len(name) == 0 || len(name) > 64 {
return false
}
for i, r := range name {
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || r == '_' || (i > 0 && r >= '0' && r <= '9') {
continue
}
return false
}
return true
}