go/keys: the version of a kept value and the key-part escaping that more than one domain shares (ADR 0634 §4; issue 1966)
Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// Package keys holds what the mesh's kept values of more than one domain share: the version a kept value carries and
|
||||
// the escaping of a name into a key part (novox/hq ADR 0634 §1, §4; they were go/delivery's and are used by the
|
||||
// Delivery, Placement and Identity and access domains alike). It names no seat and no module.
|
||||
package keys
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is the version of every kept value that carries one and the only one its readers read.
|
||||
const Version = 1
|
||||
|
||||
var nameOK = regexp.MustCompile(`^[-/_=a-zA-Z0-9]+$`)
|
||||
|
||||
// EscapeName is name as a key part: a character outside letters, digits, `-`, `/` and `_` is written `=` and its
|
||||
// two lowercase hex digits per byte, and `=` itself is escaped, so any non-empty name keys and a name of allowed
|
||||
// characters only keys as it is. UnescapeName is its inverse.
|
||||
func EscapeName(name string) string {
|
||||
var b strings.Builder
|
||||
for i := 0; i < len(name); i++ {
|
||||
c := name[i]
|
||||
switch {
|
||||
case c >= 'a' && c <= 'z', c >= 'A' && c <= 'Z', c >= '0' && c <= '9', c == '-', c == '/', c == '_':
|
||||
b.WriteByte(c)
|
||||
default:
|
||||
fmt.Fprintf(&b, "=%02x", c)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// UnescapeName is the inverse of EscapeName; it refuses a part with a bare `=` or any character EscapeName leaves
|
||||
// escaped.
|
||||
func UnescapeName(part string) (string, error) {
|
||||
var out []byte
|
||||
for i := 0; i < len(part); i++ {
|
||||
c := part[i]
|
||||
if c != '=' {
|
||||
if !nameOK.MatchString(string(c)) {
|
||||
return "", fmt.Errorf("keys: %q holds %q, which a key part escapes", part, c)
|
||||
}
|
||||
out = append(out, c)
|
||||
continue
|
||||
}
|
||||
if i+3 > len(part) {
|
||||
return "", fmt.Errorf("keys: %q ends in an incomplete escape", part)
|
||||
}
|
||||
v, err := hex.DecodeString(part[i+1 : i+3])
|
||||
if err != nil || part[i+1:i+3] != strings.ToLower(part[i+1:i+3]) {
|
||||
return "", fmt.Errorf("keys: %q holds a malformed escape %q", part, part[i:i+3])
|
||||
}
|
||||
out = append(out, v[0])
|
||||
i += 2
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNamesEscapeIntoKeyPartsAndBack(t *testing.T) {
|
||||
for _, c := range []struct{ name, esc string }{
|
||||
{"fix/1.2", "fix/1=2e2"}, {"a.b", "a=2eb"}, {"a=b", "a=3db"}, {"a+b", "a=2bb"}, {"plain-name_1/x", "plain-name_1/x"},
|
||||
{"fix/ü", "fix/=c3=bc"}, {"x y", "x=20y"},
|
||||
} {
|
||||
esc := EscapeName(c.name)
|
||||
if esc != c.esc || strings.ContainsAny(esc, ".+ ") {
|
||||
t.Errorf("escape of %q = %q, want %q", c.name, esc, c.esc)
|
||||
}
|
||||
if back, err := UnescapeName(esc); err != nil || back != c.name {
|
||||
t.Errorf("unescape(%q) = %q, %v; want %q", esc, back, err, c.name)
|
||||
}
|
||||
}
|
||||
for _, bad := range []string{"a.b", "a=", "a=2", "a=zz", "a=2E", "a b"} {
|
||||
if got, err := UnescapeName(bad); err == nil {
|
||||
t.Errorf("unescape(%q) = %q, accepted", bad, got)
|
||||
}
|
||||
}
|
||||
if Version != 1 {
|
||||
t.Errorf("Version = %d: a kept value's readers read the one version", Version)
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
// request: does every module share it, or is it a shape more than one part reads?
|
||||
var meshSDKPackages = []string{
|
||||
".", "announce", "announcetest", "buildstep", "buscalls", "buscompose", "busstate", "busretry", "calladdress", "catalogue", "channel",
|
||||
"composer", "declaration", "delivery", "forgekey", "handacts", "health", "internal/beside", "kvlimit", "membership",
|
||||
"composer", "declaration", "delivery", "forgekey", "handacts", "health", "internal/beside", "keys", "kvlimit", "membership",
|
||||
"objectlink", "outward", "overlay", "parts", "pernode", "process", "provider", "provider/harness", "provisions",
|
||||
"sdkversion", "seal", "seatdef", "seatlist", "seatlist/seatlisttest", "testbus", "views", "views/viewstest",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user