package engine import "gitea.maximumdirect.net/eric/ConfigKit/internal/document" // Metadata provides detached effective presence/origin and chronological history // queries for a load. Its zero value contains no paths. type Metadata struct { effectivePaths map[string]Path fields map[string]FieldInfo history map[string][]Change inheritance []inheritanceOperation historyBuilder *historyBuilder historyModel *historyModel } // Lookup returns information for an effective path. Unknown and invalid paths // return false. func (m Metadata) Lookup(path Path) (FieldInfo, bool) { info, ok := m.effectiveField(path) if !ok { return FieldInfo{}, false } detached := cloneFieldInfo(info) detached.Origin.Inheritance = projectInheritanceSteps(detached.Origin) return detached, true } // IsExplicit reports whether a surviving input contribution, including one // inherited from a sibling, explicitly defined path. func (m Metadata) IsExplicit(path Path) bool { info, ok := m.effectiveField(path) return ok && info.Explicit } func (m Metadata) effectiveField(path Path) (FieldInfo, bool) { if !path.valid() { return FieldInfo{}, false } pointer := path.String() candidate, ok := m.effectivePaths[pointer] if !ok || !matchesPathShape(path, candidate) { return FieldInfo{}, false } info, ok := m.fields[pointer] return info, ok } // History returns detached definition, inheritance, and resolution records for path. A record's // Path can be an ancestor when that container operation replaced or removed path. // Removed historical paths can have history even though Lookup returns false. // Unknown and invalid paths return nil. func (m Metadata) History(path Path) []Change { model := m.historyEvidence() if model == nil || !model.hasObservedShape(path) { return nil } selections := model.selectHistory(path) if len(selections) == 0 { return nil } changes := make([]Change, 0, len(selections)) for _, selection := range selections { if !metadataHistoryRole(selection.role) || !selection.hasAttribution { continue } var previous []Origin if len(selection.previous) != 0 { previous = make([]Origin, len(selection.previous)) for index, origin := range selection.previous { previous[index] = cloneOrigin(origin) } } changes = append(changes, Change{ Path: selection.reportedPath.clone(), Origin: cloneOrigin(selection.attribution.origin), Previous: previous, Reasons: append([]string(nil), selection.attribution.reasons...), Dependencies: clonePaths(selection.attribution.dependencies), }) } if len(changes) == 0 { return nil } return changes } // HistoricalPaths returns the paths observed during loading, including effective // paths and paths removed by overlays, inheritance, or recorded resolution. It includes default // paths and the root, but not paths that never existed. The zero Metadata returns // an empty slice. // // Paths are detached, unique by canonical JSON Pointer, and sorted by that // spelling. Like ParsePath results, numeric tokens are resolved contextually; // one pointer can identify a mapping key in an earlier layer and a sequence index // in a later one. Use History to inspect definitions and Lookup to check whether // a path remains effective. Enumeration exposes no configuration values and does // not perform another load or resolve secrets. func (m Metadata) HistoricalPaths() []Path { return m.historyEvidence().historicalPaths() } func (m Metadata) historyEvidence() *historyModel { if m.historyModel != nil { return m.historyModel } if m.historyBuilder != nil { return m.historyBuilder.model } return nil } // Textual pointers are lookup keys, but explicit key/index kinds remain part of // the query. Candidate paths are constructed from actual document containers. func matchesPathShape(query, candidate Path) bool { if !query.valid() || len(query.segments) != len(candidate.segments) { return false } for i, segment := range query.segments { actual := candidate.segments[i] if !pathTokenSpellingEqual(segment, actual) { return false } if actual.kind == pathSegmentIndex { switch segment.kind { case pathSegmentIndex: if segment.index != actual.index { return false } case pathSegmentParsed: index, ok := schemaIndex(segment) if !ok || index != actual.index { return false } default: return false } } else if segment.kind == pathSegmentIndex { return false } } return true } func (m Metadata) hasHistoricalPath(path Path) bool { return m.historyEvidence().hasObservedShape(path) } func finalizeMetadataPaths(m *Metadata, final *document.Node, applications []historyApplication, earlier ...*document.Node) { finalizeMetadataPathsWithResolution(m, final, applications, nil, earlier...) } func finalizeMetadataPathsWithResolution(m *Metadata, final *document.Node, applications []historyApplication, snapshots []resolutionSnapshot, earlier ...*document.Node) { finalizeEffectiveMetadataPaths(m, final) builder := m.historyBuilder if builder == nil { builder = newHistoryBuilder() } if len(earlier) != 0 { builder.model.baseline = earlier[0] } builder.model.final = final for index, tree := range earlier { phase := historyPhaseSources switch index { case 0: phase = historyPhaseBaseline case 2: phase = historyPhasePreCLI case 3: phase = historyPhasePostCLI } builder.observeDocument(tree, phase) } if final != nil { builder.observeDocument(final, historyPhasePostCLI) } for _, application := range applications { builder.registerDocumentRoot(application.value) phase := historyPhaseSources if application.layer == LayerCLI { phase = historyPhaseCLI } payload := builder.nextPayload(phase) walkHistoryApplication(application, func(_, effective Path, node *document.Node) { builder.addInventoryObservation(effective, effective, payload, phase, node.Sensitive) }) } for _, operation := range m.inheritance { payload := builder.nextPayload(historyPhaseInheritance) if operation.control != nil { builder.addInventoryObservation(operation.control.path, operation.control.path, payload, historyPhaseInheritance, false) continue } if operation.transfer == nil { continue } builder.registerDocumentRoot(operation.parent) builder.registerDocumentRoot(operation.effective) for _, root := range []*document.Node{operation.parent, operation.effective} { walkDocument(root, operation.transfer.to, func(path Path, node *document.Node) { builder.addInventoryObservation(path, path, payload, historyPhaseInheritance, node.Sensitive) }) } } if len(snapshots) != 0 { if len(earlier) != 0 { builder.setScratch(earlier[len(earlier)-1]) } builder.observeResolutionEvidence(snapshots) } m.historyModel = builder.freeze() m.historyBuilder = nil } func finalizeEffectiveMetadataPaths(m *Metadata, final *document.Node) { m.effectivePaths = effectivePathIndex(final) } // effectivePathIndex records document-derived paths with their contextual segment // kinds. Callers retain no aliases to the walked tree or its paths. func effectivePathIndex(final *document.Node) map[string]Path { paths := make(map[string]Path) walkDocument(final, Path{}, func(path Path, _ *document.Node) { paths[path.String()] = path.clone() }) return paths }