mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-09 04:20:17 +00:00
404 lines
10 KiB
Go
404 lines
10 KiB
Go
package v3
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/pb33f/libopenapi/datamodel/low"
|
|
"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]low.ObjectReference{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeMap(valueNode) {
|
|
if field.CanSet() {
|
|
items := make(map[string]low.ObjectReference)
|
|
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] = low.ObjectReference{
|
|
Value: decoded,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
}
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
|
|
case reflect.TypeOf(map[string]low.NodeReference[string]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeMap(valueNode) {
|
|
if field.CanSet() {
|
|
items := make(map[string]low.NodeReference[string])
|
|
var currentLabel string
|
|
for i, sliceItem := range valueNode.Content {
|
|
if i%2 == 0 {
|
|
currentLabel = sliceItem.Value
|
|
continue
|
|
}
|
|
items[currentLabel] = low.NodeReference[string]{
|
|
Value: sliceItem.Value,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
}
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.ObjectReference{}):
|
|
if valueNode != nil {
|
|
var decoded map[string]interface{}
|
|
err := valueNode.Decode(&decoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if utils.IsNodeMap(valueNode) {
|
|
if field.CanSet() {
|
|
or := low.ObjectReference{Value: decoded, ValueNode: valueNode}
|
|
field.Set(reflect.ValueOf(or))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.ObjectReference{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.ObjectReference
|
|
for _, sliceItem := range valueNode.Content {
|
|
var decoded map[string]interface{}
|
|
err := sliceItem.Decode(&decoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
items = append(items, low.ObjectReference{
|
|
Value: decoded,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[string]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeStringValue(valueNode) {
|
|
if field.CanSet() {
|
|
nr := low.NodeReference[string]{
|
|
Value: valueNode.Value,
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[bool]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeBoolValue(valueNode) {
|
|
if field.CanSet() {
|
|
bv, _ := strconv.ParseBool(valueNode.Value)
|
|
nr := low.NodeReference[bool]{
|
|
Value: bv,
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[int]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeIntValue(valueNode) {
|
|
if field.CanSet() {
|
|
fv, _ := strconv.Atoi(valueNode.Value)
|
|
nr := low.NodeReference[int]{
|
|
Value: fv,
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[int64]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { //
|
|
if field.CanSet() {
|
|
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
|
nr := low.NodeReference[int64]{
|
|
Value: fv,
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[float32]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeFloatValue(valueNode) {
|
|
if field.CanSet() {
|
|
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
|
nr := low.NodeReference[float32]{
|
|
Value: float32(fv),
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf(low.NodeReference[float64]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeFloatValue(valueNode) {
|
|
if field.CanSet() {
|
|
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
|
nr := low.NodeReference[float64]{
|
|
Value: fv,
|
|
ValueNode: valueNode,
|
|
KeyNode: keyNode,
|
|
}
|
|
field.Set(reflect.ValueOf(nr))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[string]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[string]
|
|
for _, sliceItem := range valueNode.Content {
|
|
items = append(items, low.NodeReference[string]{
|
|
Value: sliceItem.Value,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[float32]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[float32]
|
|
for _, sliceItem := range valueNode.Content {
|
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
|
items = append(items, low.NodeReference[float32]{
|
|
Value: float32(fv),
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[float64]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[float64]
|
|
for _, sliceItem := range valueNode.Content {
|
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
|
items = append(items, low.NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[int]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[int]
|
|
for _, sliceItem := range valueNode.Content {
|
|
iv, _ := strconv.Atoi(sliceItem.Value)
|
|
items = append(items, low.NodeReference[int]{
|
|
Value: iv,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[int64]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[int64]
|
|
for _, sliceItem := range valueNode.Content {
|
|
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
|
items = append(items, low.NodeReference[int64]{
|
|
Value: iv,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
case reflect.TypeOf([]low.NodeReference[bool]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[bool]
|
|
for _, sliceItem := range valueNode.Content {
|
|
bv, _ := strconv.ParseBool(sliceItem.Value)
|
|
items = append(items, low.NodeReference[bool]{
|
|
Value: bv,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
break
|
|
// helper for unpacking string maps.
|
|
case reflect.TypeOf(map[low.NodeReference[string]]low.NodeReference[string]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeMap(valueNode) {
|
|
if field.CanSet() {
|
|
items := make(map[low.NodeReference[string]]low.NodeReference[string])
|
|
var cf *yaml.Node
|
|
for i, sliceItem := range valueNode.Content {
|
|
if i%2 == 0 {
|
|
cf = sliceItem
|
|
continue
|
|
}
|
|
items[low.NodeReference[string]{
|
|
Value: cf.Value,
|
|
KeyNode: cf,
|
|
}] = low.NodeReference[string]{
|
|
Value: sliceItem.Value,
|
|
ValueNode: sliceItem,
|
|
}
|
|
}
|
|
field.Set(reflect.ValueOf(items))
|
|
}
|
|
}
|
|
}
|
|
case reflect.TypeOf(low.NodeReference[[]low.NodeReference[string]]{}):
|
|
if valueNode != nil {
|
|
if utils.IsNodeArray(valueNode) {
|
|
if field.CanSet() {
|
|
var items []low.NodeReference[string]
|
|
for _, sliceItem := range valueNode.Content {
|
|
items = append(items, low.NodeReference[string]{
|
|
Value: sliceItem.Value,
|
|
ValueNode: sliceItem,
|
|
KeyNode: valueNode,
|
|
})
|
|
}
|
|
n := low.NodeReference[[]low.NodeReference[string]]{
|
|
Value: items,
|
|
KeyNode: keyNode,
|
|
ValueNode: valueNode,
|
|
}
|
|
field.Set(reflect.ValueOf(n))
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
// we want to ignore everything else.
|
|
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()
|
|
}
|