ADR 0593 §7: the composer with its refusals leaves mesh-sdk, so mesh-sdk carries no composition rule. The catalogue functions only the node-engine's composition reaches (Resolve, Resolution's Compose, Declaration, DataItems, LeftOut, Rules and the rest, the settings, placeholder and template refusals, the rendering helpers) are copied with their tests into internal/compose/resolution; a method of a mesh-sdk shape becomes a function taking it first. The composition calls its own copies, and the captured catalogue its tests read lives here now, shared by mesh-compose's tests. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
76 lines
2.0 KiB
Go
76 lines
2.0 KiB
Go
package resolution
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
|
|
mcat "git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
)
|
|
|
|
// wantsHard is whether this module wants the name as a hard requirement: required without
|
|
// `optional`, or contributed to.
|
|
func manifestWantsHard(m mcat.Manifest, name string) bool {
|
|
for _, w := range m.Wants() {
|
|
if w == name {
|
|
return !m.IsOptional(name)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// AbsentFor is the optional requirements of one module this resolution leaves unbound.
|
|
func AbsentFor(r mcat.Resolution, module string) map[string]bool {
|
|
out := map[string]bool{}
|
|
for _, a := range r.Absent {
|
|
if a.Consumer == module {
|
|
out[a.Requirement] = true
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// absentOf is, for each requirement left absent, every module of the set that requires it
|
|
// optionally, sorted by consumer then requirement.
|
|
func absentOf(names map[string]bool, order []string, catalogue map[string]mcat.Manifest) []mcat.Absent {
|
|
var out []mcat.Absent
|
|
for _, module := range order {
|
|
m := catalogue[module]
|
|
for _, r := range m.Requires {
|
|
if names[r] && m.IsOptional(r) {
|
|
out = append(out, mcat.Absent{Consumer: module, Requirement: r})
|
|
}
|
|
}
|
|
}
|
|
sort.Slice(out, func(i, j int) bool {
|
|
if out[i].Consumer != out[j].Consumer {
|
|
return out[i].Consumer < out[j].Consumer
|
|
}
|
|
return out[i].Requirement < out[j].Requirement
|
|
})
|
|
return out
|
|
}
|
|
|
|
// withoutAbsent drops the resources delivered only while a requirement is bound whose requirement is
|
|
// absent here (ADR 0330 §5), and the field itself from every resource that keeps it.
|
|
func withoutAbsent(resources []map[string]any, absent map[string]bool) []map[string]any {
|
|
out := make([]map[string]any, 0, len(resources))
|
|
for _, r := range resources {
|
|
when, has := r[mcat.OnlyWhenBound]
|
|
if !has {
|
|
out = append(out, r)
|
|
continue
|
|
}
|
|
if absent[fmt.Sprint(when)] {
|
|
continue
|
|
}
|
|
kept := make(map[string]any, len(r))
|
|
for k, v := range r {
|
|
if k != mcat.OnlyWhenBound {
|
|
kept[k] = v
|
|
}
|
|
}
|
|
out = append(out, kept)
|
|
}
|
|
return out
|
|
}
|