mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
Refactoring extraction and builder functions.
now things are robust, we can move things around a little to prepare for the next set of incoming models. The extraction and builder functions have all been moved to the low packakge, and out of the v3 package.
This commit is contained in:
401
datamodel/low/model_builder.go
Normal file
401
datamodel/low/model_builder.go
Normal file
@@ -0,0 +1,401 @@
|
||||
package low
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func BuildModel(node *yaml.Node, model interface{}) error {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||
}
|
||||
v := reflect.ValueOf(model).Elem()
|
||||
num := v.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
|
||||
fName := v.Type().Field(i).Name
|
||||
|
||||
// we need to find a matching field in the YAML, the cases may be off, so take no chances.
|
||||
cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
|
||||
utils.SnakeCase, utils.KebabCase, utils.RegularCase}
|
||||
|
||||
var vn, kn *yaml.Node
|
||||
for _, tryCase := range cases {
|
||||
kn, vn = utils.FindKeyNode(utils.ConvertCase(fName, tryCase), node.Content)
|
||||
if vn != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if vn == nil {
|
||||
// no point in going on.
|
||||
continue
|
||||
}
|
||||
|
||||
field := v.FieldByName(fName)
|
||||
kind := field.Kind()
|
||||
switch kind {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||
err := SetField(field, vn, kn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
||||
switch field.Type() {
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[any])
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
var decoded map[string]interface{}
|
||||
err := sliceItem.Decode(&decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items[currentLabel] = NodeReference[any]{
|
||||
Value: decoded,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[string])
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
items[currentLabel] = NodeReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[any]{}):
|
||||
if valueNode != nil {
|
||||
var decoded interface{}
|
||||
err := valueNode.Decode(&decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
or := NodeReference[any]{Value: decoded, ValueNode: valueNode}
|
||||
field.Set(reflect.ValueOf(or))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[any]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[any]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
var decoded map[string]interface{}
|
||||
err := sliceItem.Decode(&decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items = append(items, NodeReference[any]{
|
||||
Value: decoded,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[string]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeStringValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
nr := NodeReference[string]{
|
||||
Value: valueNode.Value,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[bool]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeBoolValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
bv, _ := strconv.ParseBool(valueNode.Value)
|
||||
nr := NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[int]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeIntValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.Atoi(valueNode.Value)
|
||||
nr := NodeReference[int]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[int64]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { //
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
||||
nr := NodeReference[int64]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[float32]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeFloatValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
||||
nr := NodeReference[float32]{
|
||||
Value: float32(fv),
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf(NodeReference[float64]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeFloatValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
||||
nr := NodeReference[float64]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[string]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, NodeReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[float32]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float32]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
||||
items = append(items, NodeReference[float32]{
|
||||
Value: float32(fv),
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[float64]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
||||
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[int]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.Atoi(sliceItem.Value)
|
||||
items = append(items, NodeReference[int]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[int64]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||
items = append(items, NodeReference[int64]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case reflect.TypeOf([]NodeReference[bool]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[bool]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
bv, _ := strconv.ParseBool(sliceItem.Value)
|
||||
items = append(items, NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
// helper for unpacking string maps.
|
||||
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[KeyReference[string]]ValueReference[string])
|
||||
var cf *yaml.Node
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
cf = sliceItem
|
||||
continue
|
||||
}
|
||||
items[KeyReference[string]{
|
||||
Value: cf.Value,
|
||||
KeyNode: cf,
|
||||
}] = ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
||||
if valueNode != nil {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(n))
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
// we want to ignore everything else, each model handles its own complex types.
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
|
||||
if n != nil {
|
||||
err := BuildModel(n, model)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
}
|
||||
}
|
||||
lwg.Done()
|
||||
}
|
||||
Reference in New Issue
Block a user