package engine import ( "sort" "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 historicalPaths map[string][]Path fields map[string]FieldInfo history map[string][]Change inheritance []inheritanceOperation inheritanceIndex *inheritanceHistoryIndex } // 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 { if !m.hasHistoricalPath(path) { return nil } history := m.history[path.String()] operations := m.inheritance if m.inheritanceIndex != nil { operations = m.inheritanceIndex.matchingOperations(path, m.inheritance) } return historyWithInheritance(path, history, operations) } // 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 { pointers := make([]string, 0, len(m.historicalPaths)) for pointer := range m.historicalPaths { pointers = append(pointers, pointer) } sort.Strings(pointers) paths := make([]Path, 0, len(pointers)) for _, pointer := range pointers { // The index contains only canonical pointers produced by Path.String. path, _ := ParsePath(pointer) paths = append(paths, path) } return paths } // 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 { index, ok := schemaIndex(segment) if !ok || index != actual.index { return false } } else if segment.kind == pathSegmentIndex { return false } } return true } func (m Metadata) hasHistoricalPath(path Path) bool { if !path.valid() { return false } for _, candidate := range m.historicalPaths[path.String()] { if matchesPathShape(path, candidate) { return true } } return false } func finalizeMetadataPaths(m *Metadata, final *document.Node, applications []historyApplication, earlier ...*document.Node) { m.effectivePaths = make(map[string]Path) m.historicalPaths = make(map[string][]Path) addHistorical := func(path Path, pointer string) { for _, prior := range m.historicalPaths[pointer] { if matchesPathShape(path, prior) { return } } m.historicalPaths[pointer] = append(m.historicalPaths[pointer], path.clone()) } walkDocument(final, Path{}, func(path Path, _ *document.Node) { pointer := path.String() m.effectivePaths[pointer] = path.clone() addHistorical(path, pointer) }) add := func(path Path, _ *document.Node) { addHistorical(path, path.String()) } for _, tree := range earlier { walkDocument(tree, Path{}, add) } for _, application := range applications { // Overrides have mapping-only prefixes; parsed tokens are keys in this layer. prefix := Path{} for _, segment := range application.path.segments { prefix = prefix.Key(segment.key) } walkDocument(application.value, prefix, add) } for _, operation := range m.inheritance { if operation.control != nil { add(operation.control.path, nil) continue } if operation.transfer != nil { walkDocument(operation.parent, operation.transfer.to, add) walkDocument(operation.effective, operation.transfer.to, add) } } m.inheritanceIndex = newInheritanceHistoryIndex(m.inheritance) } // 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 }