Support integer schema versions through typed decoding
This commit is contained in:
56
decode.go
56
decode.go
@@ -34,7 +34,15 @@ type decodedConfiguration struct {
|
||||
final *document.Node
|
||||
}
|
||||
|
||||
type decodeContext struct {
|
||||
versionPath *Path
|
||||
}
|
||||
|
||||
func decodeConfiguration(schema *schemaDescriptor, defaults reflect.Value, input *document.Node) (decodedConfiguration, []decodeIssue) {
|
||||
return decodeConfigurationWithContext(schema, defaults, input, decodeContext{})
|
||||
}
|
||||
|
||||
func decodeConfigurationWithContext(schema *schemaDescriptor, defaults reflect.Value, input *document.Node, context decodeContext) (decodedConfiguration, []decodeIssue) {
|
||||
if schema == nil || schema.root == nil || !defaults.IsValid() || defaults.Type() != schema.root.typeOf {
|
||||
return decodedConfiguration{}, []decodeIssue{{code: CodeSchemaUnsupportedType, message: "configuration defaults do not match the schema"}}
|
||||
}
|
||||
@@ -50,7 +58,7 @@ func decodeConfiguration(schema *schemaDescriptor, defaults reflect.Value, input
|
||||
presence := make(map[string]inputPresence)
|
||||
if input != nil {
|
||||
collectPresence(input, Path{}, presence)
|
||||
decoded, issues := decodeValue(input, working, schema.root, Path{}, schema)
|
||||
decoded, issues := decodeValue(input, working, schema.root, Path{}, schema, context)
|
||||
if len(issues) != 0 {
|
||||
return decodedConfiguration{}, issues
|
||||
}
|
||||
@@ -71,7 +79,7 @@ func decodeConfiguration(schema *schemaDescriptor, defaults reflect.Value, input
|
||||
}, nil
|
||||
}
|
||||
|
||||
func decodeValue(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeValue(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind == document.Null {
|
||||
switch descriptor.kind {
|
||||
case descriptorPointer, descriptorMap, descriptorSlice, descriptorDynamic:
|
||||
@@ -89,7 +97,7 @@ func decodeValue(node *document.Node, base reflect.Value, descriptor *typeDescri
|
||||
} else {
|
||||
elementBase = base.Elem()
|
||||
}
|
||||
decoded, issues := decodeValue(node, elementBase, descriptor.element, path, schema)
|
||||
decoded, issues := decodeValue(node, elementBase, descriptor.element, path, schema, context)
|
||||
if len(issues) != 0 {
|
||||
return reflect.Value{}, issues
|
||||
}
|
||||
@@ -100,17 +108,17 @@ func decodeValue(node *document.Node, base reflect.Value, descriptor *typeDescri
|
||||
|
||||
switch descriptor.kind {
|
||||
case descriptorStruct:
|
||||
return decodeStruct(node, base, descriptor, path, schema)
|
||||
return decodeStruct(node, base, descriptor, path, schema, context)
|
||||
case descriptorMap:
|
||||
return decodeMap(node, base, descriptor, path, schema)
|
||||
return decodeMap(node, base, descriptor, path, schema, context)
|
||||
case descriptorSlice:
|
||||
return decodeSlice(node, descriptor, path, schema)
|
||||
return decodeSlice(node, descriptor, path, schema, context)
|
||||
case descriptorArray:
|
||||
return decodeArray(node, descriptor, path, schema)
|
||||
return decodeArray(node, descriptor, path, schema, context)
|
||||
case descriptorDynamic:
|
||||
return decodeDynamic(node, base, path, schema)
|
||||
return decodeDynamic(node, base, path, schema, context)
|
||||
case descriptorString:
|
||||
if node.Kind != document.String {
|
||||
if node.Kind != document.String && !(pathMatches(context.versionPath, path) && isIntegerVersion(node)) {
|
||||
return typeMismatch(path, node, "string")
|
||||
}
|
||||
result := reflect.New(descriptor.typeOf).Elem()
|
||||
@@ -187,7 +195,7 @@ func decodeValue(node *document.Node, base reflect.Value, descriptor *typeDescri
|
||||
}
|
||||
}
|
||||
|
||||
func decodeStruct(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeStruct(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind != document.Mapping {
|
||||
return typeMismatch(path, node, "mapping")
|
||||
}
|
||||
@@ -203,7 +211,7 @@ func decodeStruct(node *document.Node, base reflect.Value, descriptor *typeDescr
|
||||
issues = append(issues, decodeIssue{code: CodeSchemaUnsupportedType, path: childPath, message: "configuration field could not be addressed", location: entry.ValueLocation})
|
||||
continue
|
||||
}
|
||||
decoded, fieldIssues := decodeValue(entry.Value, target, field.descriptor, childPath, schema)
|
||||
decoded, fieldIssues := decodeValue(entry.Value, target, field.descriptor, childPath, schema, context)
|
||||
if len(fieldIssues) != 0 {
|
||||
issues = append(issues, fieldIssues...)
|
||||
continue
|
||||
@@ -212,7 +220,7 @@ func decodeStruct(node *document.Node, base reflect.Value, descriptor *typeDescr
|
||||
continue
|
||||
}
|
||||
if descriptor.inlineMap != nil {
|
||||
if issue := decodeInlineMapEntry(result, *descriptor.inlineMap, entry, childPath, schema); issue != nil {
|
||||
if issue := decodeInlineMapEntry(result, *descriptor.inlineMap, entry, childPath, schema, context); issue != nil {
|
||||
issues = append(issues, issue...)
|
||||
}
|
||||
continue
|
||||
@@ -222,7 +230,7 @@ func decodeStruct(node *document.Node, base reflect.Value, descriptor *typeDescr
|
||||
return result, issues
|
||||
}
|
||||
|
||||
func decodeInlineMapEntry(target reflect.Value, field fieldDescriptor, entry document.Entry, path Path, schema *schemaDescriptor) []decodeIssue {
|
||||
func decodeInlineMapEntry(target reflect.Value, field fieldDescriptor, entry document.Entry, path Path, schema *schemaDescriptor, context decodeContext) []decodeIssue {
|
||||
mapValue, ok := fieldForDecode(target, field.index, true)
|
||||
if !ok || mapValue.Kind() != reflect.Map {
|
||||
return []decodeIssue{{code: CodeSchemaInline, path: path, message: "inline map could not be addressed", location: entry.KeyLocation}}
|
||||
@@ -236,14 +244,14 @@ func decodeInlineMapEntry(target reflect.Value, field fieldDescriptor, entry doc
|
||||
if !base.IsValid() {
|
||||
base = reflect.Zero(mapValue.Type().Elem())
|
||||
}
|
||||
decoded, issues := decodeValue(entry.Value, base, field.descriptor.element, path, schema)
|
||||
decoded, issues := decodeValue(entry.Value, base, field.descriptor.element, path, schema, context)
|
||||
if len(issues) == 0 {
|
||||
mapValue.SetMapIndex(key, decoded)
|
||||
}
|
||||
return issues
|
||||
}
|
||||
|
||||
func decodeMap(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeMap(node *document.Node, base reflect.Value, descriptor *typeDescriptor, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind != document.Mapping {
|
||||
return typeMismatch(path, node, "mapping")
|
||||
}
|
||||
@@ -259,7 +267,7 @@ func decodeMap(node *document.Node, base reflect.Value, descriptor *typeDescript
|
||||
if !entryBase.IsValid() {
|
||||
entryBase = reflect.Zero(descriptor.typeOf.Elem())
|
||||
}
|
||||
decoded, entryIssues := decodeValue(entry.Value, entryBase, descriptor.element, path.Key(entry.Key), schema)
|
||||
decoded, entryIssues := decodeValue(entry.Value, entryBase, descriptor.element, path.Key(entry.Key), schema, context)
|
||||
if len(entryIssues) != 0 {
|
||||
issues = append(issues, entryIssues...)
|
||||
continue
|
||||
@@ -269,14 +277,14 @@ func decodeMap(node *document.Node, base reflect.Value, descriptor *typeDescript
|
||||
return result, issues
|
||||
}
|
||||
|
||||
func decodeSlice(node *document.Node, descriptor *typeDescriptor, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeSlice(node *document.Node, descriptor *typeDescriptor, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind != document.Sequence {
|
||||
return typeMismatch(path, node, "sequence")
|
||||
}
|
||||
result := reflect.MakeSlice(descriptor.typeOf, len(node.Sequence), len(node.Sequence))
|
||||
var issues []decodeIssue
|
||||
for i, child := range node.Sequence {
|
||||
decoded, childIssues := decodeValue(child, reflect.Zero(descriptor.typeOf.Elem()), descriptor.element, path.Index(i), schema)
|
||||
decoded, childIssues := decodeValue(child, reflect.Zero(descriptor.typeOf.Elem()), descriptor.element, path.Index(i), schema, context)
|
||||
if len(childIssues) != 0 {
|
||||
issues = append(issues, childIssues...)
|
||||
continue
|
||||
@@ -286,7 +294,7 @@ func decodeSlice(node *document.Node, descriptor *typeDescriptor, path Path, sch
|
||||
return result, issues
|
||||
}
|
||||
|
||||
func decodeArray(node *document.Node, descriptor *typeDescriptor, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeArray(node *document.Node, descriptor *typeDescriptor, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind != document.Sequence {
|
||||
return typeMismatch(path, node, "sequence")
|
||||
}
|
||||
@@ -296,7 +304,7 @@ func decodeArray(node *document.Node, descriptor *typeDescriptor, path Path, sch
|
||||
result := reflect.New(descriptor.typeOf).Elem()
|
||||
var issues []decodeIssue
|
||||
for i, child := range node.Sequence {
|
||||
decoded, childIssues := decodeValue(child, reflect.Zero(descriptor.typeOf.Elem()), descriptor.element, path.Index(i), schema)
|
||||
decoded, childIssues := decodeValue(child, reflect.Zero(descriptor.typeOf.Elem()), descriptor.element, path.Index(i), schema, context)
|
||||
if len(childIssues) != 0 {
|
||||
issues = append(issues, childIssues...)
|
||||
continue
|
||||
@@ -306,13 +314,13 @@ func decodeArray(node *document.Node, descriptor *typeDescriptor, path Path, sch
|
||||
return result, issues
|
||||
}
|
||||
|
||||
func decodeDynamic(node *document.Node, base reflect.Value, path Path, schema *schemaDescriptor) (reflect.Value, []decodeIssue) {
|
||||
func decodeDynamic(node *document.Node, base reflect.Value, path Path, schema *schemaDescriptor, context decodeContext) (reflect.Value, []decodeIssue) {
|
||||
if node.Kind == document.Mapping && base.IsValid() && !base.IsNil() {
|
||||
concrete := base.Elem()
|
||||
operation := valueOperation{schema: schema}
|
||||
descriptor, issue := operation.dynamicDescriptor(concrete.Type(), path)
|
||||
if issue == nil && acceptsMapping(descriptor) {
|
||||
decoded, issues := decodeValue(node, concrete, descriptor, path, schema)
|
||||
decoded, issues := decodeValue(node, concrete, descriptor, path, schema, context)
|
||||
if len(issues) != 0 {
|
||||
return reflect.Value{}, issues
|
||||
}
|
||||
@@ -347,7 +355,7 @@ func decodeDynamic(node *document.Node, base reflect.Value, path Path, schema *s
|
||||
sequence := make([]any, len(node.Sequence))
|
||||
var issues []decodeIssue
|
||||
for i, child := range node.Sequence {
|
||||
decoded, childIssues := decodeDynamic(child, reflect.Zero(reflect.TypeFor[any]()), path.Index(i), schema)
|
||||
decoded, childIssues := decodeDynamic(child, reflect.Zero(reflect.TypeFor[any]()), path.Index(i), schema, context)
|
||||
if len(childIssues) != 0 {
|
||||
issues = append(issues, childIssues...)
|
||||
continue
|
||||
@@ -362,7 +370,7 @@ func decodeDynamic(node *document.Node, base reflect.Value, path Path, schema *s
|
||||
mapping := make(map[string]any, len(node.Mapping))
|
||||
var issues []decodeIssue
|
||||
for _, entry := range node.Mapping {
|
||||
decoded, childIssues := decodeDynamic(entry.Value, reflect.Zero(reflect.TypeFor[any]()), path.Key(entry.Key), schema)
|
||||
decoded, childIssues := decodeDynamic(entry.Value, reflect.Zero(reflect.TypeFor[any]()), path.Key(entry.Key), schema, context)
|
||||
if len(childIssues) != 0 {
|
||||
issues = append(issues, childIssues...)
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user