okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffaufrefactor(cli): apply Go modernize simplifications02d2013

main
3.8 KiB143 linesraw
1package daemon
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7	"strconv"
8	"strings"
9
10	"albedo/cli/internal/daemon/protocol"
11)
12
13// Binding keys are canonical JSON pointers. Reject ancestor assignments before
14// resolving optional values so omission cannot make an ambiguous form valid.
15func validateBodyBindings(bindings map[string]protocol.Binding) error {
16	for pointer := range bindings {
17		if !strings.HasPrefix(pointer, "/") {
18			return errors.New("body binding keys must be JSON pointers")
19		}
20		for i := 0; i < len(pointer); i++ {
21			switch pointer[i] {
22			case '~':
23				if i+1 == len(pointer) || pointer[i+1] != '0' && pointer[i+1] != '1' {
24					return fmt.Errorf("invalid JSON pointer escape in %q", pointer)
25				}
26				i++
27			case '/':
28				if i > 0 {
29					if _, exists := bindings[pointer[:i]]; exists {
30						return errors.New("overlapping body bindings")
31					}
32				}
33			}
34		}
35	}
36	return nil
37}
38
39func bindingText(raw json.RawMessage) (string, error) {
40	var text string
41	if json.Unmarshal(raw, &text) == nil {
42		return text, nil
43	}
44	var scalar any
45	if json.Unmarshal(raw, &scalar) != nil {
46		return "", errors.New("invalid binding value")
47	}
48	switch scalar.(type) {
49	case bool, float64:
50		return string(raw), nil
51	}
52	return "", errors.New("path, query, and header bindings require scalar values")
53}
54func resolveBinding(raw, row, form, session json.RawMessage) (json.RawMessage, error) {
55	var binding struct {
56		Source  string          `json:"source"`
57		Value   json.RawMessage `json:"value"`
58		Pointer string          `json:"pointer"`
59	}
60	if err := json.Unmarshal(raw, &binding); err != nil {
61		return nil, err
62	}
63	switch binding.Source {
64	case "literal":
65		if binding.Value == nil {
66			return nil, errors.New("literal binding requires a value")
67		}
68		return binding.Value, nil
69	case "row":
70		raw = row
71	case "form":
72		raw = form
73	case "session":
74		raw = session
75	default:
76		return nil, errors.New("unknown binding source")
77	}
78	if raw == nil {
79		return nil, errors.New("action requires its displayed row or session")
80	}
81	if binding.Pointer == "" {
82		return raw, nil
83	}
84	if !strings.HasPrefix(binding.Pointer, "/") {
85		return nil, errors.New("invalid JSON pointer")
86	}
87	for token := range strings.SplitSeq(binding.Pointer[1:], "/") {
88		token = strings.ReplaceAll(strings.ReplaceAll(token, "~1", "/"), "~0", "~")
89		if len(raw) > 0 && raw[0] == '[' {
90			var array []json.RawMessage
91			if err := json.Unmarshal(raw, &array); err != nil {
92				return nil, err
93			}
94			index, err := strconv.Atoi(token)
95			if err != nil || index < 0 || index >= len(array) {
96				return nil, errors.New("binding array index is missing")
97			}
98			raw = array[index]
99		} else {
100			var object map[string]json.RawMessage
101			if err := json.Unmarshal(raw, &object); err != nil {
102				return nil, errors.New("binding pointer does not name an object field")
103			}
104			var exists bool
105			raw, exists = object[token]
106			if !exists {
107				if binding.Source == "form" {
108					return nil, &missingFormBindingError{Field: token}
109				}
110				return nil, fmt.Errorf("binding field %q is missing", token)
111			}
112		}
113	}
114	return raw, nil
115}
116func setBodyPointer(root map[string]any, pointer string, value any) error {
117	if !strings.HasPrefix(pointer, "/") {
118		return errors.New("body binding keys must be JSON pointers")
119	}
120	tokens := strings.Split(pointer[1:], "/")
121	current := root
122	for i, token := range tokens {
123		token = strings.ReplaceAll(strings.ReplaceAll(token, "~1", "/"), "~0", "~")
124		if i == len(tokens)-1 {
125			current[token] = value
126			return nil
127		}
128		child, ok := current[token].(map[string]any)
129		if !ok {
130			if _, exists := current[token]; exists {
131				return errors.New("overlapping body bindings")
132			}
133			child = map[string]any{}
134			current[token] = child
135		}
136		current = child
137	}
138	return nil
139}
140
141type missingFormBindingError struct{ Field string }
142
143func (e *missingFormBindingError) Error() string { return "form field " + e.Field + " is omitted" }