package compositions import ( "encoding/json" "fmt" "sort" seat "git.novox.be/novox/mesh-contracts/go/seats/nodeengine" "git.novox.be/novox/mesh-sdk/go/declaration" ) // PackagesBucket is the node-engine's state packages (the seat's state of the same name), one key per node. const PackagesBucket = "mesh-host_" + seat.StatePackages // DeclaredPackages is a node's key of `packages` from the compositions its engine composed for the node's wants: // each resource of type package, by its name and whether it is declared absent, with the modules that declare it so, // in the mirror's layout and order (names sorted, absent after present, modules sorted). A resource's module is the // one it names, else its composition's, else the guess from its id (declaration.ModuleGuess); a package of no module // lists none. func DeclaredPackages(node string, comps []Composed) (seat.PackagesByNodeValue, error) { by := map[packageKey]map[string]bool{} for _, c := range comps { for _, raw := range c.Resources { var r struct { ID string `json:"id"` Type string `json:"type"` Package string `json:"package"` Absent bool `json:"absent"` Module string `json:"module"` } if err := json.Unmarshal(raw, &r); err != nil { return seat.PackagesByNodeValue{}, fmt.Errorf("compositions: a resource of %s's composition %q is not readable: %w", node, c.Module, err) } if r.Type != "package" || r.Package == "" { continue } k := packageKey{r.Package, r.Absent} if by[k] == nil { by[k] = map[string]bool{} } module := r.Module if module == "" { module = c.Module } if module == "" { module, _ = declaration.ModuleGuess(r.ID) } if module != "" { by[k][module] = true } } } v := seat.PackagesByNodeValue{Node: node, Packages: []seat.PackagesByNodeValuePackagesItem{}} for _, k := range sortedPackageKeys(by) { modules := make([]string, 0, len(by[k])) for m := range by[k] { modules = append(modules, m) } sort.Strings(modules) v.Packages = append(v.Packages, seat.PackagesByNodeValuePackagesItem{Package: k.name, Modules: modules, Absent: k.absent}) } return v, nil } // packageKey is a package as the layout tells it apart: its name, and whether it is declared absent. type packageKey struct { name string absent bool } // sortedPackageKeys is the keys of m by name, absent after present. func sortedPackageKeys[V any](m map[packageKey]V) []packageKey { keys := make([]packageKey, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Slice(keys, func(i, j int) bool { if keys[i].name != keys[j].name { return keys[i].name < keys[j].name } return !keys[i].absent && keys[j].absent }) return keys }