Read placement from a layer's Placement, never from its config values (issue 651)
ADR 0348 clause 11: places, accesses and kept networks are placement, not config. Today's mergeable files still take them as said, so nothing composes differently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,69 @@ const PlacesSetting = "places"
|
|||||||
// AccessesSetting is the settings key that says where a module's accesses are on this node, by id.
|
// AccessesSetting is the settings key that says where a module's accesses are on this node, by id.
|
||||||
const AccessesSetting = "accesses"
|
const AccessesSetting = "accesses"
|
||||||
|
|
||||||
|
// **Placement is not config** (novox/hq ADR 0348 clause 11, issue 651). `places`, `accesses` and
|
||||||
|
// `networks` are facts of an assignment the controller decides, held beside the assignment and apart
|
||||||
|
// from its settings: a Layer carries them in Placement, never in Values, and only Places, AccessPlaces
|
||||||
|
// and KeptNetworks read them. A person still says them as keys of a layer through `settings`; LayerOf
|
||||||
|
// splits such a layer, so what they type is routed to the placement and nothing they do changes.
|
||||||
|
|
||||||
|
// placementKeys are the keys of a layer that are placement, not config.
|
||||||
|
var placementKeys = map[string]bool{PlacesSetting: true, AccessesSetting: true, NetworksSetting: true}
|
||||||
|
|
||||||
|
// SplitPlacement divides a layer as a person says it into its config values and its placement. Each is
|
||||||
|
// nil when it holds nothing; the values themselves are not copied.
|
||||||
|
func SplitPlacement(values map[string]any) (config, placement map[string]any) {
|
||||||
|
for key, value := range values {
|
||||||
|
if placementKeys[key] {
|
||||||
|
if placement == nil {
|
||||||
|
placement = map[string]any{}
|
||||||
|
}
|
||||||
|
placement[key] = value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if config == nil {
|
||||||
|
config = map[string]any{}
|
||||||
|
}
|
||||||
|
config[key] = value
|
||||||
|
}
|
||||||
|
return config, placement
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerOf is a layer as a person says it — config values and placement in one map — split into the two.
|
||||||
|
func LayerOf(from string, values map[string]any) Layer {
|
||||||
|
config, placement := SplitPlacement(values)
|
||||||
|
if config == nil {
|
||||||
|
config = map[string]any{}
|
||||||
|
}
|
||||||
|
return Layer{From: from, Values: config, Placement: placement}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whole is the layer as a person says it: its values and its placement in one map, a new map.
|
||||||
|
func (l Layer) Whole() map[string]any {
|
||||||
|
out := make(map[string]any, len(l.Values)+len(l.Placement))
|
||||||
|
for k, v := range l.Values {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range l.Placement {
|
||||||
|
out[k] = v
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// asSaid is the layers with placement folded back into the values, for what reads a layer whole as it
|
||||||
|
// did while placement was kept in it: today's mergeable file, which takes every key (ADR 0326 §3), and
|
||||||
|
// the judgement that no value holds a line end.
|
||||||
|
func asSaid(layers []Layer) []Layer {
|
||||||
|
out := make([]Layer, len(layers))
|
||||||
|
for i, l := range layers {
|
||||||
|
out[i] = l
|
||||||
|
if len(l.Placement) > 0 {
|
||||||
|
out[i] = Layer{From: l.From, Values: l.Whole(), Default: l.Default}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// Placement is what an assignment says about one of a module's directories.
|
// Placement is what an assignment says about one of a module's directories.
|
||||||
type Placement struct {
|
type Placement struct {
|
||||||
// Path is where the directory is on this machine. Absolute.
|
// Path is where the directory is on this machine. Absolute.
|
||||||
@@ -208,7 +271,7 @@ func Places(m Manifest, layers []Layer) (map[string]Placement, error) {
|
|||||||
}
|
}
|
||||||
out := map[string]Placement{}
|
out := map[string]Placement{}
|
||||||
for _, layer := range layers {
|
for _, layer := range layers {
|
||||||
raw, ok := layer.Values[PlacesSetting]
|
raw, ok := layer.Placement[PlacesSetting]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -278,7 +341,7 @@ func AccessPlaces(m Manifest, layers []Layer) (map[string]string, error) {
|
|||||||
}
|
}
|
||||||
out := map[string]string{}
|
out := map[string]string{}
|
||||||
for _, layer := range layers {
|
for _, layer := range layers {
|
||||||
raw, ok := layer.Values[AccessesSetting]
|
raw, ok := layer.Placement[AccessesSetting]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,8 @@ func Effective(m Manifest, layers []Layer) []SettingSource {
|
|||||||
Default: d.Default, HasDefault: true}
|
Default: d.Default, HasDefault: true}
|
||||||
}
|
}
|
||||||
for _, layer := range layers {
|
for _, layer := range layers {
|
||||||
for key, v := range layer.Values {
|
// As the layer is said, placement too: `settings` answers it as a person set it.
|
||||||
|
for key, v := range layer.Whole() {
|
||||||
s, ok := byKey[key]
|
s, ok := byKey[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
s = &SettingSource{Key: key}
|
s = &SettingSource{Key: key}
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ type Layer struct {
|
|||||||
// Default marks the layer a module's own defaults make (novox/hq ADR 0262). Marked rather than
|
// Default marks the layer a module's own defaults make (novox/hq ADR 0262). Marked rather than
|
||||||
// recognised by From, which is a node's name for a node's layer, and a node may be called anything.
|
// recognised by From, which is a node's name for a node's layer, and a node may be called anything.
|
||||||
Default bool
|
Default bool
|
||||||
|
// Placement is the assignment's placement at this layer's scope — `places`, `accesses`, `networks`
|
||||||
|
// — which the controller holds apart from settings (novox/hq ADR 0348 clause 11, issue 651): read
|
||||||
|
// by Places, AccessPlaces and KeptNetworks, and never from Values. LayerOf splits a layer as a person
|
||||||
|
// says it into the two.
|
||||||
|
Placement map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplySettings produces a resource's final content from the module's own and the layers over it.
|
// ApplySettings produces a resource's final content from the module's own and the layers over it.
|
||||||
@@ -45,6 +50,10 @@ type Layer struct {
|
|||||||
// module first, then this node's. A node that differs from the rest is expressed by differing,
|
// module first, then this node's. A node that differs from the rest is expressed by differing,
|
||||||
// rather than by repeating everything the rest already say.
|
// rather than by repeating everything the rest already say.
|
||||||
func ApplySettings(resource map[string]any, layers []Layer) (map[string]any, error) {
|
func ApplySettings(resource map[string]any, layers []Layer) (map[string]any, error) {
|
||||||
|
// Today's form reaches a mergeable file with every key of the layer as it was said, placement too
|
||||||
|
// (novox/hq ADR 0326 §3, ADR 0348 clause 11: a module not yet moved keeps it), so a file composes
|
||||||
|
// byte for byte as it did while placement was kept in the layer.
|
||||||
|
layers = asSaid(layers)
|
||||||
how, _ := resource["merge"].(string)
|
how, _ := resource["merge"].(string)
|
||||||
taken, names := takenKeys(resource)
|
taken, names := takenKeys(resource)
|
||||||
if how == "" {
|
if how == "" {
|
||||||
@@ -395,7 +404,7 @@ func UnusedSettings(m Manifest, layers []Layer) []string {
|
|||||||
}
|
}
|
||||||
var unused []string
|
var unused []string
|
||||||
for _, layer := range layers {
|
for _, layer := range layers {
|
||||||
for key := range layer.Values {
|
for key := range layer.Whole() {
|
||||||
if lands[key] {
|
if lands[key] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -483,7 +492,7 @@ func KeptNetworks(m Manifest, layers []Layer, adopted bool) (map[string][]string
|
|||||||
}
|
}
|
||||||
out := map[string][]string{}
|
out := map[string][]string{}
|
||||||
for _, layer := range layers {
|
for _, layer := range layers {
|
||||||
raw, ok := layer.Values[NetworksSetting]
|
raw, ok := layer.Placement[NetworksSetting]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -543,7 +552,8 @@ var networkName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_.-]*$`)
|
|||||||
// refused where it is stored (SetSettings, with UnusedSettings) and said where a plan is read,
|
// refused where it is stored (SetSettings, with UnusedSettings) and said where a plan is read,
|
||||||
// and never costs a module its place.
|
// and never costs a module its place.
|
||||||
func JudgeSettings(m Manifest, layers []Layer, adopted bool) error {
|
func JudgeSettings(m Manifest, layers []Layer, adopted bool) error {
|
||||||
if err := settingsHoldOneLine(m.Module, layers); err != nil {
|
// Placement is judged for line ends as it always was: a path is written into files as well.
|
||||||
|
if err := settingsHoldOneLine(m.Module, asSaid(layers)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Which keys each mergeable file takes is the definition's to say right (novox/hq ADR 0326);
|
// Which keys each mergeable file takes is the definition's to say right (novox/hq ADR 0326);
|
||||||
|
|||||||
Reference in New Issue
Block a user