Separate reflection projection handlers
This commit is contained in:
@@ -39,89 +39,9 @@ func (o *valueOperation) project(value reflect.Value, descriptor *typeDescriptor
|
||||
|
||||
switch descriptor.kind {
|
||||
case descriptorStruct:
|
||||
result, issue := o.node(document.Mapping, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = make([]document.Entry, 0, len(descriptor.fields))
|
||||
seen := make(map[string]bool, len(descriptor.fields))
|
||||
for _, member := range descriptor.members {
|
||||
memberValue := value.Field(member.index)
|
||||
if member.inline {
|
||||
if isNilValue(memberValue) {
|
||||
continue
|
||||
}
|
||||
inline, issue := o.project(memberValue, member.descriptor, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
if inline.Kind != document.Mapping {
|
||||
return nil, &projectionIssue{code: CodeSchemaUnsupportedType, path: path.clone(), message: "inline value did not project to a mapping"}
|
||||
}
|
||||
o.nodes-- // The flattened container is not retained in the output tree.
|
||||
for _, entry := range inline.Mapping {
|
||||
if seen[entry.Key] {
|
||||
return nil, &projectionIssue{code: CodeSchemaAmbiguousField, path: path.Key(entry.Key), message: "inline value conflicts with a declared field"}
|
||||
}
|
||||
seen[entry.Key] = true
|
||||
result.Mapping = append(result.Mapping, entry)
|
||||
}
|
||||
continue
|
||||
}
|
||||
childPath := path.Key(member.name)
|
||||
if seen[member.name] {
|
||||
return nil, &projectionIssue{code: CodeSchemaAmbiguousField, path: childPath, message: "inline value conflicts with a declared field"}
|
||||
}
|
||||
if issue := o.charge(childPath); issue != nil { // mapping key
|
||||
return nil, issue
|
||||
}
|
||||
child, issue := o.project(memberValue, member.descriptor, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
seen[member.name] = true
|
||||
result.Mapping = append(result.Mapping, document.Entry{Key: member.name, Value: child})
|
||||
}
|
||||
for _, entry := range result.Mapping {
|
||||
field := descriptor.inlineMap
|
||||
if index, ok := descriptor.fieldByName[entry.Key]; ok {
|
||||
field = &descriptor.fields[index]
|
||||
}
|
||||
if field != nil && field.secret {
|
||||
entry.Value.Sensitive = true
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
return o.projectStruct(value, descriptor, path, depth)
|
||||
case descriptorMap:
|
||||
if value.IsNil() {
|
||||
return o.node(document.Null, path, depth)
|
||||
}
|
||||
leave, issue := o.enter(value, path)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
defer leave()
|
||||
result, issue := o.node(document.Mapping, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = make([]document.Entry, 0, value.Len())
|
||||
keys := value.MapKeys()
|
||||
slices.SortFunc(keys, func(left, right reflect.Value) int {
|
||||
return cmp.Compare(left.String(), right.String())
|
||||
})
|
||||
for _, key := range keys {
|
||||
childPath := path.Key(key.String())
|
||||
if issue := o.charge(childPath); issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
child, issue := o.project(value.MapIndex(key), descriptor.element, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = append(result.Mapping, document.Entry{Key: key.String(), Value: child})
|
||||
}
|
||||
return result, nil
|
||||
return o.projectMap(value, descriptor, path, depth)
|
||||
case descriptorSlice:
|
||||
if value.IsNil() {
|
||||
return o.node(document.Null, path, depth)
|
||||
@@ -131,22 +51,9 @@ func (o *valueOperation) project(value reflect.Value, descriptor *typeDescriptor
|
||||
return nil, issue
|
||||
}
|
||||
defer leave()
|
||||
fallthrough
|
||||
return o.projectSequence(value, descriptor, path, depth)
|
||||
case descriptorArray:
|
||||
result, issue := o.node(document.Sequence, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Sequence = make([]*document.Node, 0, value.Len())
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
childPath := path.Index(i)
|
||||
child, issue := o.project(value.Index(i), descriptor.element, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Sequence = append(result.Sequence, child)
|
||||
}
|
||||
return result, nil
|
||||
return o.projectSequence(value, descriptor, path, depth)
|
||||
case descriptorDynamic:
|
||||
if value.IsNil() {
|
||||
return o.node(document.Null, path, depth)
|
||||
@@ -217,6 +124,111 @@ func (o *valueOperation) project(value reflect.Value, descriptor *typeDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
func (o *valueOperation) projectStruct(value reflect.Value, descriptor *typeDescriptor, path Path, depth int) (*document.Node, *projectionIssue) {
|
||||
result, issue := o.node(document.Mapping, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = make([]document.Entry, 0, len(descriptor.fields))
|
||||
seen := make(map[string]bool, len(descriptor.fields))
|
||||
for _, member := range descriptor.members {
|
||||
memberValue := value.Field(member.index)
|
||||
if member.inline {
|
||||
if isNilValue(memberValue) {
|
||||
continue
|
||||
}
|
||||
inline, issue := o.project(memberValue, member.descriptor, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
if inline.Kind != document.Mapping {
|
||||
return nil, &projectionIssue{code: CodeSchemaUnsupportedType, path: path.clone(), message: "inline value did not project to a mapping"}
|
||||
}
|
||||
o.nodes-- // The flattened container is not retained in the output tree.
|
||||
for _, entry := range inline.Mapping {
|
||||
if seen[entry.Key] {
|
||||
return nil, &projectionIssue{code: CodeSchemaAmbiguousField, path: path.Key(entry.Key), message: "inline value conflicts with a declared field"}
|
||||
}
|
||||
seen[entry.Key] = true
|
||||
result.Mapping = append(result.Mapping, entry)
|
||||
}
|
||||
continue
|
||||
}
|
||||
childPath := path.Key(member.name)
|
||||
if seen[member.name] {
|
||||
return nil, &projectionIssue{code: CodeSchemaAmbiguousField, path: childPath, message: "inline value conflicts with a declared field"}
|
||||
}
|
||||
if issue := o.charge(childPath); issue != nil { // mapping key
|
||||
return nil, issue
|
||||
}
|
||||
child, issue := o.project(memberValue, member.descriptor, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
seen[member.name] = true
|
||||
result.Mapping = append(result.Mapping, document.Entry{Key: member.name, Value: child})
|
||||
}
|
||||
for _, entry := range result.Mapping {
|
||||
field := descriptor.inlineMap
|
||||
if index, ok := descriptor.fieldByName[entry.Key]; ok {
|
||||
field = &descriptor.fields[index]
|
||||
}
|
||||
if field != nil && field.secret {
|
||||
entry.Value.Sensitive = true
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *valueOperation) projectMap(value reflect.Value, descriptor *typeDescriptor, path Path, depth int) (*document.Node, *projectionIssue) {
|
||||
if value.IsNil() {
|
||||
return o.node(document.Null, path, depth)
|
||||
}
|
||||
leave, issue := o.enter(value, path)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
defer leave()
|
||||
result, issue := o.node(document.Mapping, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = make([]document.Entry, 0, value.Len())
|
||||
keys := value.MapKeys()
|
||||
slices.SortFunc(keys, func(left, right reflect.Value) int {
|
||||
return cmp.Compare(left.String(), right.String())
|
||||
})
|
||||
for _, key := range keys {
|
||||
childPath := path.Key(key.String())
|
||||
if issue := o.charge(childPath); issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
child, issue := o.project(value.MapIndex(key), descriptor.element, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Mapping = append(result.Mapping, document.Entry{Key: key.String(), Value: child})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *valueOperation) projectSequence(value reflect.Value, descriptor *typeDescriptor, path Path, depth int) (*document.Node, *projectionIssue) {
|
||||
result, issue := o.node(document.Sequence, path, depth)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Sequence = make([]*document.Node, 0, value.Len())
|
||||
for index := 0; index < value.Len(); index++ {
|
||||
childPath := path.Index(index)
|
||||
child, issue := o.project(value.Index(index), descriptor.element, childPath, depth+1)
|
||||
if issue != nil {
|
||||
return nil, issue
|
||||
}
|
||||
result.Sequence = append(result.Sequence, child)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (o *valueOperation) node(kind document.Kind, path Path, depth int) (*document.Node, *projectionIssue) {
|
||||
if issue := o.checkDepth(path, depth); issue != nil {
|
||||
return nil, issue
|
||||
|
||||
Reference in New Issue
Block a user