104 lines
3.4 KiB
Go
104 lines
3.4 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// TrustedField is the key a file resource carries to say whether the settings it asks for are trusted: true, or
|
|
// absent, makes each a terminal key; false says, out loud, that none changes what root or a consumer trusts. Said in the
|
|
// catalogue, never on the machine: the composer takes it out before the node-engine, which parses strictly.
|
|
const TrustedField = "trusted"
|
|
|
|
// TerminalKeys are the settings keys of a module that are set at the controller's terminal alone: places and
|
|
// accesses, every key its provisions serve and every setting a served value asks for, every setting a file
|
|
// asks for unless it says `"trusted": false`, and every setting a directory's owner asks for. Places and accesses first, then the rest sorted.
|
|
func TerminalKeys(m Manifest) []string {
|
|
keys := map[string]bool{}
|
|
for _, served := range m.Serves {
|
|
for key, value := range served {
|
|
keys[key] = true
|
|
if s, ok := value.(string); ok {
|
|
for _, asked := range settingsUsed(s) {
|
|
keys[asked] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) == "directory" {
|
|
owner, _ := r["owner"].(string)
|
|
for _, asked := range settingsUsed(owner) {
|
|
keys[asked] = true
|
|
}
|
|
continue
|
|
}
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
continue
|
|
}
|
|
if trusted, said := r[TrustedField].(bool); said && !trusted {
|
|
continue
|
|
}
|
|
for _, asked := range asksFor(r) {
|
|
keys[asked] = true
|
|
}
|
|
}
|
|
delete(keys, PlacesSetting)
|
|
delete(keys, AccessesSetting)
|
|
rest := make([]string, 0, len(keys))
|
|
for k := range keys {
|
|
rest = append(rest, k)
|
|
}
|
|
sort.Strings(rest)
|
|
return append([]string{PlacesSetting, AccessesSetting}, rest...)
|
|
}
|
|
|
|
// UnsaidTrust is every file of a module that asks for a setting and does not say whether it is trusted, by id:
|
|
// each counts as trusted, and is listed so an author can opt out a file nothing trusts.
|
|
func UnsaidTrust(m Manifest) []string {
|
|
var out []string
|
|
for _, r := range m.Resources {
|
|
if fmt.Sprint(r["type"]) != "file" {
|
|
continue
|
|
}
|
|
if len(asksFor(r)) == 0 {
|
|
continue
|
|
}
|
|
if _, said := r[TrustedField]; !said {
|
|
out = append(out, fmt.Sprint(r["id"]))
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// asksFor is every setting a file resource asks for: each `${setting:…}` in its content and, for a mergeable file,
|
|
// every key at the top of the content it merges into (novox/hq issue 340). Those are the keys the file declares it
|
|
// takes: a layer's value for one of them lands in it as surely as a placeholder would be filled. A key the content
|
|
// does not name may land too, but nothing reading the file was written to read it.
|
|
func asksFor(r map[string]any) []string {
|
|
content, _ := r["content"].(string)
|
|
asked := settingsUsed(content)
|
|
if how, _ := r["merge"].(string); how != "" {
|
|
if _, names := takenKeys(r); names {
|
|
// The file takes exactly the keys it names (ADR 0326), whatever its content holds, and a renamed
|
|
// one under the file's own spelling too, which a layer said before its module moved (issue 751).
|
|
asked = append(asked, takenNames(r)...)
|
|
sort.Strings(asked)
|
|
return asked
|
|
}
|
|
}
|
|
if how, _ := r["merge"].(string); how != "" && strings.TrimSpace(content) != "" {
|
|
var base map[string]any
|
|
if json.Unmarshal([]byte(content), &base) == nil {
|
|
for key := range base {
|
|
asked = append(asked, key)
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(asked)
|
|
return asked
|
|
}
|