package configkit 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 historicalPaths map[string][]Path fields map[string]FieldInfo history map[string][]Change } // Lookup returns information for an effective path. Unknown and invalid paths // return false. func (m Metadata) Lookup(path Path) (FieldInfo, bool) { if !path.valid() || !matchesPathShape(path, m.effectivePaths[path.String()]) { return FieldInfo{}, false } info, ok := m.fields[path.String()] if !ok { return FieldInfo{}, false } return cloneFieldInfo(info), true } // IsExplicit reports whether a surviving input contribution explicitly defined // path. func (m Metadata) IsExplicit(path Path) bool { info, ok := m.Lookup(path) return ok && info.Explicit } // History returns detached definition 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, ok := m.history[path.String()] if !ok { return nil } cloned := make([]Change, len(history)) for i, change := range history { cloned[i] = cloneChange(change) } return cloned } // 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() || query.String() != candidate.String() || len(query.segments) != len(candidate.segments) { return false } for i, segment := range query.segments { actual := candidate.segments[i] 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) walkDocument(final, Path{}, func(path Path, _ *document.Node) { m.effectivePaths[path.String()] = path.clone() }) add := func(path Path, _ *document.Node) { pointer := path.String() for _, prior := range m.historicalPaths[pointer] { if matchesPathShape(path, prior) { return } } m.historicalPaths[pointer] = append(m.historicalPaths[pointer], path.clone()) } walkDocument(final, Path{}, add) 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) } }