Add semantic paths and safe diagnostics
This commit is contained in:
167
records.go
Normal file
167
records.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package configkit
|
||||
|
||||
// Layer identifies the precedence layer responsible for a configuration value.
|
||||
type Layer string
|
||||
|
||||
const (
|
||||
LayerDefault Layer = "default"
|
||||
LayerBase Layer = "base"
|
||||
LayerProfile Layer = "profile"
|
||||
LayerCLI Layer = "cli"
|
||||
LayerResolution Layer = "resolution"
|
||||
)
|
||||
|
||||
// ChangeKind identifies an application resolution operation. The zero value is
|
||||
// used for input and default origins.
|
||||
type ChangeKind string
|
||||
|
||||
const (
|
||||
ChangeDerivedDefault ChangeKind = "derived_default"
|
||||
ChangeNormalization ChangeKind = "normalization"
|
||||
)
|
||||
|
||||
// Severity classifies a diagnostic.
|
||||
type Severity string
|
||||
|
||||
const (
|
||||
SeverityError Severity = "error"
|
||||
SeverityWarning Severity = "warning"
|
||||
)
|
||||
|
||||
// Code is a stable, machine-readable diagnostic classification.
|
||||
type Code string
|
||||
|
||||
// Location identifies a position in a configuration source. Zero line and column
|
||||
// values mean that coordinates are unavailable or synthetic.
|
||||
type Location struct {
|
||||
SourceID string
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
// SourceInfo describes a source discovered during a load. Path is a canonical
|
||||
// local path when one exists. ImportedFrom identifies the declaration that
|
||||
// included this source.
|
||||
type SourceInfo struct {
|
||||
ID string
|
||||
Label string
|
||||
Path string
|
||||
Layer Layer
|
||||
ImportedFrom *Location
|
||||
}
|
||||
|
||||
// Diagnostic is a safe structured problem report. Message and source labels must
|
||||
// never contain configuration values or other secrets.
|
||||
type Diagnostic struct {
|
||||
Code Code
|
||||
Severity Severity
|
||||
Path Path
|
||||
Message string
|
||||
Primary *Location
|
||||
Related []Location
|
||||
}
|
||||
|
||||
// Origin describes where an effective definition came from. Multiple locations
|
||||
// represent a composite origin.
|
||||
type Origin struct {
|
||||
Layer Layer
|
||||
Locations []Location
|
||||
Operation ChangeKind
|
||||
}
|
||||
|
||||
// FieldInfo describes an effective configuration path.
|
||||
type FieldInfo struct {
|
||||
Explicit bool
|
||||
Null bool
|
||||
Origin Origin
|
||||
}
|
||||
|
||||
// Change describes a definition or application-resolution operation. Historical
|
||||
// values are intentionally not exposed by this record.
|
||||
type Change struct {
|
||||
Path Path
|
||||
Origin Origin
|
||||
Previous []Origin
|
||||
Reasons []string
|
||||
Dependencies []Path
|
||||
}
|
||||
|
||||
func cloneLocation(location *Location) *Location {
|
||||
if location == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := *location
|
||||
return &cloned
|
||||
}
|
||||
|
||||
func cloneLocations(locations []Location) []Location {
|
||||
if locations == nil {
|
||||
return nil
|
||||
}
|
||||
return append([]Location(nil), locations...)
|
||||
}
|
||||
|
||||
func cloneSourceInfo(source SourceInfo) SourceInfo {
|
||||
source.ImportedFrom = cloneLocation(source.ImportedFrom)
|
||||
return source
|
||||
}
|
||||
|
||||
func cloneSourceInfos(sources []SourceInfo) []SourceInfo {
|
||||
if sources == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := make([]SourceInfo, len(sources))
|
||||
for i, source := range sources {
|
||||
cloned[i] = cloneSourceInfo(source)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
func cloneOrigin(origin Origin) Origin {
|
||||
origin.Locations = cloneLocations(origin.Locations)
|
||||
return origin
|
||||
}
|
||||
|
||||
func cloneFieldInfo(info FieldInfo) FieldInfo {
|
||||
info.Origin = cloneOrigin(info.Origin)
|
||||
return info
|
||||
}
|
||||
|
||||
func cloneChange(change Change) Change {
|
||||
change.Path = change.Path.clone()
|
||||
change.Origin = cloneOrigin(change.Origin)
|
||||
if change.Previous != nil {
|
||||
previous := make([]Origin, len(change.Previous))
|
||||
for i, origin := range change.Previous {
|
||||
previous[i] = cloneOrigin(origin)
|
||||
}
|
||||
change.Previous = previous
|
||||
}
|
||||
change.Reasons = append([]string(nil), change.Reasons...)
|
||||
if change.Dependencies != nil {
|
||||
dependencies := make([]Path, len(change.Dependencies))
|
||||
for i, path := range change.Dependencies {
|
||||
dependencies[i] = path.clone()
|
||||
}
|
||||
change.Dependencies = dependencies
|
||||
}
|
||||
return change
|
||||
}
|
||||
|
||||
func cloneDiagnostic(diagnostic Diagnostic) Diagnostic {
|
||||
diagnostic.Path = diagnostic.Path.clone()
|
||||
diagnostic.Primary = cloneLocation(diagnostic.Primary)
|
||||
diagnostic.Related = cloneLocations(diagnostic.Related)
|
||||
return diagnostic
|
||||
}
|
||||
|
||||
func cloneDiagnostics(diagnostics []Diagnostic) []Diagnostic {
|
||||
if diagnostics == nil {
|
||||
return nil
|
||||
}
|
||||
cloned := make([]Diagnostic, len(diagnostics))
|
||||
for i, diagnostic := range diagnostics {
|
||||
cloned[i] = cloneDiagnostic(diagnostic)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
Reference in New Issue
Block a user