Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations, about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs, CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
123 lines
4.3 KiB
Go
123 lines
4.3 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// A provider may tell its consumers its own composed names (novox/hq ADR 0346, issue 624).
|
|
//
|
|
// **The name is already the mesh's.** A module contributes a label to a requirement — a route — and
|
|
// the mesh joins it with the providing machine's domains; the module reads the result in its own files
|
|
// as `${bound:<requirement>:name}` (novox/hq 04-ISSUES/122). Its consumers could not be told it: a
|
|
// served value named only the consumer (ADR 0202) and settings. The forge's package registry writes
|
|
// every tarball at its public name, and a consumer that must trust that name had no source for it but a
|
|
// second copy kept by hand. So a served value may say `${bound:<requirement>:<name>}` for a requirement
|
|
// the provider contributes to, and the mesh fills it from the same composition the provider's own files
|
|
// and its proxy receive.
|
|
//
|
|
// **A name the providing machine does not compose leaves the key out** (ADR 0346 §3): a machine with
|
|
// no public domain has no public name to give, and an empty value or a refusal would be worse than a
|
|
// consumer told nothing — a refused provider machine takes every provision it offers down with it. The
|
|
// plan says which key was left out, and why, on the provider's machine and under each consumer's need.
|
|
|
|
// requirementsContributedTo is every requirement m contributes to, sorted.
|
|
func requirementsContributedTo(m Manifest) []string {
|
|
seen := map[string]bool{}
|
|
for to := range m.Contributes {
|
|
seen[to] = true
|
|
}
|
|
for to := range m.ContributesMany {
|
|
seen[to] = true
|
|
}
|
|
out := make([]string, 0, len(seen))
|
|
for to := range seen {
|
|
out = append(out, to)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|
|
|
|
// ServedOwnNames fills the provider's own composed names into what it serves one provision's
|
|
// consumers, from this resolution's composition of the provider's machine (ADR 0346).
|
|
//
|
|
// A value naming a name this machine does not compose is left out, with its key, and said. The map is
|
|
// copied only when something changes, as ServedTo does: the caller's may be the manifest's.
|
|
func (r Resolution) ServedOwnNames(m Manifest, provision string, serves map[string]any, layers []Layer) (
|
|
map[string]any, []string, error) {
|
|
var asking []string
|
|
for _, key := range sortedAnyKeys(serves) {
|
|
if text, ok := serves[key].(string); ok && bound.MatchString(text) {
|
|
asking = append(asking, key)
|
|
}
|
|
}
|
|
if len(asking) == 0 {
|
|
return serves, nil, nil
|
|
}
|
|
own := map[string]map[string]string{}
|
|
for _, to := range requirementsContributedTo(m) {
|
|
names, err := r.ownNames(m, to, layers)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
values := map[string]string{}
|
|
withOwnNames(values, names)
|
|
own[to] = values
|
|
}
|
|
out := make(map[string]any, len(serves))
|
|
for k, v := range serves {
|
|
out[k] = v
|
|
}
|
|
var said []string
|
|
for _, key := range asking {
|
|
text := serves[key].(string)
|
|
var missing string
|
|
filled := bound.ReplaceAllStringFunc(text, func(token string) string {
|
|
parts := bound.FindStringSubmatch(token)
|
|
if value := own[parts[1]][parts[2]]; value != "" && parts[3] == "" {
|
|
return value
|
|
}
|
|
if missing == "" {
|
|
missing = token
|
|
}
|
|
return token
|
|
})
|
|
if missing != "" {
|
|
delete(out, key)
|
|
said = append(said, fmt.Sprintf(
|
|
"%s serves %s without %q: it names %s, and %s composes no such name for %s (it has no "+
|
|
"public domain, or the endpoint is not reached publicly) (novox/hq ADR 0346)",
|
|
m.Module, provision, key, missing, orHere(r.Node), m.Module))
|
|
continue
|
|
}
|
|
out[key] = filled
|
|
}
|
|
return out, said, nil
|
|
}
|
|
|
|
func orHere(node string) string {
|
|
if node == "" {
|
|
return "this machine"
|
|
}
|
|
return node
|
|
}
|
|
|
|
// ServedLeftOut is what the providers in this node's own set leave out of what they serve, because
|
|
// this machine composes no name a served value asks for (ADR 0346 §3): said on the provider's machine,
|
|
// where the remedy is.
|
|
func (r Resolution) ServedLeftOut(settings SettingsBy) []string {
|
|
var said []string
|
|
for _, m := range r.Modules {
|
|
for _, provision := range sortedServes(m.Serves) {
|
|
_, lines, err := r.ServedOwnNames(m, provision, m.Serves[provision], WithDefaults(m, settings[m.Module]))
|
|
if err != nil {
|
|
said = append(said, fmt.Sprintf("%s serves %s, and its own names could not be composed: %v",
|
|
m.Module, provision, err))
|
|
continue
|
|
}
|
|
said = append(said, lines...)
|
|
}
|
|
}
|
|
return said
|
|
}
|