// Package meshconfigclient is the logic every reader of the seat mesh-config's state kept shares (novox/hq ADR 0565 // clause 5, ADR 0634 ยง4): the checker and the assigner's composition check lay a pull request's own held values over its // modules' values. The layout of a value is the seat's contract's; this holds what the readers share over it: a pull // request's key, a held value's key and the rule that a value is the one its key names. package meshconfigclient import ( "encoding/json" "fmt" "strings" seat "git.novox.be/novox/mesh-contracts/go/seats/meshconfig" ) // PullKey is a pull request's key, `owner/repository#number` with `/` and `#` as dots, the form the seat keeps a pull // request's values under. The three parts hold no dot, star, angle bracket or space. func PullKey(pull string) (string, error) { owner, repository, number, err := splitPull(pull) if err != nil { return "", err } return owner + "." + repository + "." + number, nil } func splitPull(pull string) (owner, repository, number string, err error) { owner, rest, ok := strings.Cut(strings.TrimSpace(pull), "/") repository, number, ok2 := strings.Cut(rest, "#") if !ok || !ok2 || owner == "" || repository == "" || number == "" || strings.ContainsAny(owner+repository+number, ".*> ") { return "", "", "", fmt.Errorf("meshconfig: %q is not a pull request, owner/repository#number", pull) } return owner, repository, number, nil } // HeldValueKey is a held value's key in the state kept: the pull request's key, the node, the module and the config key, // joined by dots. func HeldValueKey(pull, node, module, key string) (string, error) { owner, repository, number, err := splitPull(pull) if err != nil { return "", err } if node == "" || module == "" || key == "" { return "", fmt.Errorf("meshconfig: a held value names a node, a module and a key") } return seat.KeptByOwnerRepositoryNumberNodeModuleKeyKey(owner, repository, number, node, module, key), nil } // Kept is one held value, as the seat types it. type Kept = seat.KeptByOwnerRepositoryNumberNodeModuleKeyValue // CheckKept refuses a held value that names no pull request, head, declaration, node, module or key. func CheckKept(v Kept) error { if _, err := PullKey(v.Pull); err != nil { return err } if v.Head == "" || v.Declaration == "" || v.Node == "" || v.Module == "" || v.Key == "" { return fmt.Errorf("meshconfig: a held value of %s names a head, a declaration, a node, a module and a key", v.Pull) } return nil } // DecodeKept reads a value of the state kept under its key: the value the seat types, checked, and the one its key // names. A value kept under another key than its own fields make is refused, so a reader never lays a value over the // wrong pull request or module. func DecodeKept(key string, raw []byte) (Kept, error) { v, err := seat.DecodeKeptByOwnerRepositoryNumberNodeModuleKeyValue(raw) if err != nil { return Kept{}, fmt.Errorf("meshconfig: the held value %s is not one: %w", key, err) } if err := CheckKept(v); err != nil { return Kept{}, err } want, err := HeldValueKey(v.Pull, v.Node, v.Module, v.Key) if err != nil { return Kept{}, err } if want != key { return Kept{}, fmt.Errorf("meshconfig: the held value kept under %s names %s", key, want) } return v, nil } // ValueOf is the value held, as the operator approved it, in its JSON form. func ValueOf(v Kept) (json.RawMessage, error) { return json.Marshal(v.Value) }