mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-01 02:30:18 +00:00
914 lines
34 KiB
Go
914 lines
34 KiB
Go
|
// Copyright 2017 Google Inc. All Rights Reserved.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"regexp"
|
||
|
"sort"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/googleapis/gnostic/printer"
|
||
|
)
|
||
|
|
||
|
// patternNames hands out unique names for a given string.
|
||
|
type patternNames struct {
|
||
|
prefix string
|
||
|
values map[string]int
|
||
|
last int
|
||
|
|
||
|
specialCase map[string]func(variable string) string
|
||
|
}
|
||
|
|
||
|
// SpecialCaseExpression returns true if the provided regex can be inlined as a faster
|
||
|
// expression.
|
||
|
func (p *patternNames) SpecialCaseExpression(value, variable string) (code string, ok bool) {
|
||
|
fn, ok := p.specialCase[value]
|
||
|
if !ok {
|
||
|
return "", false
|
||
|
}
|
||
|
return fn(variable), ok
|
||
|
}
|
||
|
|
||
|
// VariableName returns the variable name for the given value.
|
||
|
func (p *patternNames) VariableName(value string) string {
|
||
|
num, ok := p.values[value]
|
||
|
if !ok {
|
||
|
if p.values == nil {
|
||
|
p.values = make(map[string]int)
|
||
|
}
|
||
|
num = p.last
|
||
|
p.last++
|
||
|
p.values[value] = num
|
||
|
}
|
||
|
return fmt.Sprintf("%s%d", p.prefix, num)
|
||
|
}
|
||
|
|
||
|
func (p *patternNames) Names() map[string]string {
|
||
|
names := make(map[string]string)
|
||
|
for value, num := range p.values {
|
||
|
names[fmt.Sprintf("%s%d", p.prefix, num)] = value
|
||
|
}
|
||
|
return names
|
||
|
}
|
||
|
|
||
|
// GenerateCompiler generates the compiler code for a domain.
|
||
|
func (domain *Domain) GenerateCompiler(packageName string, license string, imports []string) string {
|
||
|
code := &printer.Code{}
|
||
|
code.Print(license)
|
||
|
code.Print("// THIS FILE IS AUTOMATICALLY GENERATED.\n")
|
||
|
|
||
|
// generate package declaration
|
||
|
code.Print("package %s\n", packageName)
|
||
|
|
||
|
code.Print("import (")
|
||
|
for _, filename := range imports {
|
||
|
code.Print("\"" + filename + "\"")
|
||
|
}
|
||
|
code.Print(")\n")
|
||
|
|
||
|
// generate a simple Version() function
|
||
|
code.Print("// Version returns the package name (and OpenAPI version).")
|
||
|
code.Print("func Version() string {")
|
||
|
code.Print(" return \"%s\"", packageName)
|
||
|
code.Print("}\n")
|
||
|
|
||
|
typeNames := domain.sortedTypeNames()
|
||
|
|
||
|
regexPatterns := &patternNames{
|
||
|
prefix: "pattern",
|
||
|
specialCase: map[string]func(string) string{
|
||
|
"^x-": func(variable string) string { return fmt.Sprintf("strings.HasPrefix(%s, \"x-\")", variable) },
|
||
|
"^/": func(variable string) string { return fmt.Sprintf("strings.HasPrefix(%s, \"/\")", variable) },
|
||
|
"^": func(_ string) string { return "true" },
|
||
|
},
|
||
|
}
|
||
|
|
||
|
// generate NewX() constructor functions for each type
|
||
|
for _, typeName := range typeNames {
|
||
|
domain.generateConstructorForType(code, typeName, regexPatterns)
|
||
|
}
|
||
|
|
||
|
// generate ResolveReferences() methods for each type
|
||
|
for _, typeName := range typeNames {
|
||
|
domain.generateResolveReferencesMethodsForType(code, typeName)
|
||
|
}
|
||
|
|
||
|
// generate ToRawInfo() methods for each type
|
||
|
for _, typeName := range typeNames {
|
||
|
domain.generateToRawInfoMethodForType(code, typeName)
|
||
|
}
|
||
|
|
||
|
domain.generateConstantVariables(code, regexPatterns)
|
||
|
|
||
|
return code.String()
|
||
|
}
|
||
|
|
||
|
func escapeSlashes(pattern string) string {
|
||
|
return strings.Replace(pattern, "\\", "\\\\", -1)
|
||
|
}
|
||
|
|
||
|
var subpatternPattern = regexp.MustCompile("^.*(\\{.*\\}).*$")
|
||
|
|
||
|
func nameForPattern(regexPatterns *patternNames, pattern string) string {
|
||
|
if !strings.HasPrefix(pattern, "^") {
|
||
|
if matches := subpatternPattern.FindStringSubmatch(pattern); matches != nil {
|
||
|
match := string(matches[1])
|
||
|
pattern = strings.Replace(pattern, match, ".*", -1)
|
||
|
}
|
||
|
}
|
||
|
return regexPatterns.VariableName(pattern)
|
||
|
}
|
||
|
|
||
|
func (domain *Domain) generateConstructorForType(code *printer.Code, typeName string, regexPatterns *patternNames) {
|
||
|
code.Print("// New%s creates an object of type %s if possible, returning an error if not.", typeName, typeName)
|
||
|
code.Print("func New%s(in interface{}, context *compiler.Context) (*%s, error) {", typeName, typeName)
|
||
|
code.Print("errors := make([]error, 0)")
|
||
|
|
||
|
typeModel := domain.TypeModels[typeName]
|
||
|
parentTypeName := typeName
|
||
|
|
||
|
if typeModel.IsStringArray {
|
||
|
code.Print("x := &TypeItem{}")
|
||
|
code.Print("switch in := in.(type) {")
|
||
|
code.Print("case string:")
|
||
|
code.Print(" x.Value = make([]string, 0)")
|
||
|
code.Print(" x.Value = append(x.Value, in)")
|
||
|
code.Print("case []interface{}:")
|
||
|
code.Print(" x.Value = make([]string, 0)")
|
||
|
code.Print(" for _, v := range in {")
|
||
|
code.Print(" value, ok := v.(string)")
|
||
|
code.Print(" if ok {")
|
||
|
code.Print(" x.Value = append(x.Value, value)")
|
||
|
code.Print(" } else {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for string array element: %%+v (%%T)\", value, value)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print(" }")
|
||
|
code.Print(" }")
|
||
|
code.Print("default:")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for string array: %%+v (%%T)\", in, in)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
} else if typeModel.IsItemArray {
|
||
|
if domain.Version == "v2" {
|
||
|
code.Print("x := &ItemsItem{}")
|
||
|
code.Print("m, ok := compiler.UnpackMap(in)")
|
||
|
code.Print("if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for item array: %%+v (%%T)\", in, in)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("} else {")
|
||
|
code.Print(" x.Schema = make([]*Schema, 0)")
|
||
|
code.Print(" y, err := NewSchema(m, compiler.NewContext(\"<array>\", context))")
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print(" }")
|
||
|
code.Print(" x.Schema = append(x.Schema, y)")
|
||
|
code.Print("}")
|
||
|
} else if domain.Version == "v3" {
|
||
|
code.Print("x := &ItemsItem{}")
|
||
|
code.Print("m, ok := compiler.UnpackMap(in)")
|
||
|
code.Print("if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for item array: %%+v (%%T)\", in, in)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("} else {")
|
||
|
code.Print(" x.SchemaOrReference = make([]*SchemaOrReference, 0)")
|
||
|
code.Print(" y, err := NewSchemaOrReference(m, compiler.NewContext(\"<array>\", context))")
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print(" }")
|
||
|
code.Print(" x.SchemaOrReference = append(x.SchemaOrReference, y)")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
} else if typeModel.IsBlob {
|
||
|
code.Print("x := &Any{}")
|
||
|
code.Print("bytes, _ := yaml.Marshal(in)")
|
||
|
code.Print("x.Yaml = string(bytes)")
|
||
|
} else if typeModel.Name == "StringArray" {
|
||
|
code.Print("x := &StringArray{}")
|
||
|
code.Print("a, ok := in.([]interface{})")
|
||
|
code.Print("if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for StringArray: %%+v (%%T)\", in, in)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("} else {")
|
||
|
code.Print(" x.Value = make([]string, 0)")
|
||
|
code.Print(" for _, s := range a {")
|
||
|
code.Print(" x.Value = append(x.Value, s.(string))")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
} else if typeModel.Name == "Primitive" {
|
||
|
code.Print(" x := &Primitive{}")
|
||
|
code.Print(" matched := false")
|
||
|
code.Print(" switch in := in.(type) {")
|
||
|
code.Print(" case bool:")
|
||
|
code.Print(" x.Oneof = &Primitive_Boolean{Boolean: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case string:")
|
||
|
code.Print(" x.Oneof = &Primitive_String_{String_: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int64:")
|
||
|
code.Print(" x.Oneof = &Primitive_Integer{Integer: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int32:")
|
||
|
code.Print(" x.Oneof = &Primitive_Integer{Integer: int64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int:")
|
||
|
code.Print(" x.Oneof = &Primitive_Integer{Integer: int64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float64:")
|
||
|
code.Print(" x.Oneof = &Primitive_Number{Number: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float32:")
|
||
|
code.Print(" x.Oneof = &Primitive_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" }")
|
||
|
code.Print(" if matched {")
|
||
|
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
|
||
|
code.Print(" errors = make([]error, 0)")
|
||
|
code.Print(" }")
|
||
|
} else if typeModel.Name == "SpecificationExtension" {
|
||
|
code.Print(" x := &SpecificationExtension{}")
|
||
|
code.Print(" matched := false")
|
||
|
code.Print(" switch in := in.(type) {")
|
||
|
code.Print(" case bool:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Boolean{Boolean: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case string:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_String_{String_: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int64:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int32:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float64:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float32:")
|
||
|
code.Print(" x.Oneof = &SpecificationExtension_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" }")
|
||
|
code.Print(" if matched {")
|
||
|
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
|
||
|
code.Print(" errors = make([]error, 0)")
|
||
|
code.Print(" }")
|
||
|
} else if typeModel.Name == "DefaultType" {
|
||
|
code.Print(" x := &DefaultType{}")
|
||
|
code.Print(" matched := false")
|
||
|
code.Print(" switch in := in.(type) {")
|
||
|
code.Print(" case bool:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Boolean{Boolean: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case string:")
|
||
|
code.Print(" x.Oneof = &DefaultType_String_{String_: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int64:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int32:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case int:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float64:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Number{Number: in}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" case float32:")
|
||
|
code.Print(" x.Oneof = &DefaultType_Number{Number: float64(in)}")
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" }")
|
||
|
code.Print(" if matched {")
|
||
|
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
|
||
|
code.Print(" errors = make([]error, 0)")
|
||
|
code.Print(" }")
|
||
|
} else {
|
||
|
oneOfWrapper := typeModel.OneOfWrapper
|
||
|
|
||
|
code.Print("x := &%s{}", typeName)
|
||
|
|
||
|
if oneOfWrapper {
|
||
|
code.Print("matched := false")
|
||
|
}
|
||
|
|
||
|
unpackAtTop := !oneOfWrapper || len(typeModel.Required) > 0
|
||
|
if unpackAtTop {
|
||
|
code.Print("m, ok := compiler.UnpackMap(in)")
|
||
|
code.Print("if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value: %%+v (%%T)\", in, in)")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("} else {")
|
||
|
}
|
||
|
if len(typeModel.Required) > 0 {
|
||
|
// verify that map includes all required keys
|
||
|
keyString := ""
|
||
|
sort.Strings(typeModel.Required)
|
||
|
for _, k := range typeModel.Required {
|
||
|
if keyString != "" {
|
||
|
keyString += ","
|
||
|
}
|
||
|
keyString += "\""
|
||
|
keyString += k
|
||
|
keyString += "\""
|
||
|
}
|
||
|
code.Print("requiredKeys := []string{%s}", keyString)
|
||
|
code.Print("missingKeys := compiler.MissingKeysInMap(m, requiredKeys)")
|
||
|
code.Print("if len(missingKeys) > 0 {")
|
||
|
code.Print(" message := fmt.Sprintf(\"is missing required %%s: %%+v\", compiler.PluralProperties(len(missingKeys)), strings.Join(missingKeys, \", \"))")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
if !typeModel.Open {
|
||
|
// verify that map has no unspecified keys
|
||
|
allowedKeys := make([]string, 0)
|
||
|
for _, property := range typeModel.Properties {
|
||
|
if !property.Implicit {
|
||
|
allowedKeys = append(allowedKeys, property.Name)
|
||
|
}
|
||
|
}
|
||
|
sort.Strings(allowedKeys)
|
||
|
allowedKeyString := ""
|
||
|
for _, allowedKey := range allowedKeys {
|
||
|
if allowedKeyString != "" {
|
||
|
allowedKeyString += ","
|
||
|
}
|
||
|
allowedKeyString += "\""
|
||
|
allowedKeyString += allowedKey
|
||
|
allowedKeyString += "\""
|
||
|
}
|
||
|
allowedPatternString := ""
|
||
|
if typeModel.OpenPatterns != nil {
|
||
|
for _, pattern := range typeModel.OpenPatterns {
|
||
|
if allowedPatternString != "" {
|
||
|
allowedPatternString += ","
|
||
|
}
|
||
|
allowedPatternString += nameForPattern(regexPatterns, pattern)
|
||
|
}
|
||
|
}
|
||
|
// verify that map includes only allowed keys and patterns
|
||
|
code.Print("allowedKeys := []string{%s}", allowedKeyString)
|
||
|
if len(allowedPatternString) > 0 {
|
||
|
code.Print("allowedPatterns := []*regexp.Regexp{%s}", allowedPatternString)
|
||
|
} else {
|
||
|
code.Print("var allowedPatterns []*regexp.Regexp")
|
||
|
|
||
|
}
|
||
|
code.Print("invalidKeys := compiler.InvalidKeysInMap(m, allowedKeys, allowedPatterns)")
|
||
|
code.Print("if len(invalidKeys) > 0 {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has invalid %%s: %%+v\", compiler.PluralProperties(len(invalidKeys)), strings.Join(invalidKeys, \", \"))")
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
var fieldNumber = 0
|
||
|
for _, propertyModel := range typeModel.Properties {
|
||
|
propertyName := propertyModel.Name
|
||
|
fieldNumber++
|
||
|
propertyType := propertyModel.Type
|
||
|
if propertyType == "int" {
|
||
|
propertyType = "int64"
|
||
|
}
|
||
|
var displayName = propertyName
|
||
|
if displayName == "$ref" {
|
||
|
displayName = "_ref"
|
||
|
}
|
||
|
if displayName == "$schema" {
|
||
|
displayName = "_schema"
|
||
|
}
|
||
|
displayName = camelCaseToSnakeCase(displayName)
|
||
|
|
||
|
var line = fmt.Sprintf("%s %s = %d;", propertyType, displayName, fieldNumber)
|
||
|
if propertyModel.Repeated {
|
||
|
line = "repeated " + line
|
||
|
}
|
||
|
code.Print("// " + line)
|
||
|
|
||
|
fieldName := strings.Title(snakeCaseToCamelCase(propertyName))
|
||
|
if propertyName == "$ref" {
|
||
|
fieldName = "XRef"
|
||
|
}
|
||
|
|
||
|
typeModel, typeFound := domain.TypeModels[propertyType]
|
||
|
if typeFound && !typeModel.IsPair {
|
||
|
if propertyModel.Repeated {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" // repeated %s", typeModel.Name)
|
||
|
code.Print(" x.%s = make([]*%s, 0)", fieldName, typeModel.Name)
|
||
|
code.Print(" a, ok := v%d.([]interface{})", fieldNumber)
|
||
|
code.Print(" if ok {")
|
||
|
code.Print(" for _, item := range a {")
|
||
|
code.Print(" y, err := New%s(item, compiler.NewContext(\"%s\", context))", typeModel.Name, propertyName)
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" }")
|
||
|
code.Print(" x.%s = append(x.%s, y)", fieldName, fieldName)
|
||
|
code.Print(" }")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
if oneOfWrapper {
|
||
|
code.Print("{")
|
||
|
if !unpackAtTop {
|
||
|
code.Print(" m, ok := compiler.UnpackMap(in)")
|
||
|
code.Print(" if ok {")
|
||
|
}
|
||
|
code.Print(" // errors might be ok here, they mean we just don't have the right subtype")
|
||
|
code.Print(" t, matchingError := New%s(m, compiler.NewContext(\"%s\", context))", typeModel.Name, propertyName)
|
||
|
code.Print(" if matchingError == nil {")
|
||
|
code.Print(" x.Oneof = &%s_%s{%s: t}", parentTypeName, typeModel.Name, typeModel.Name)
|
||
|
code.Print(" matched = true")
|
||
|
code.Print(" } else {")
|
||
|
code.Print(" errors = append(errors, matchingError)")
|
||
|
code.Print(" }")
|
||
|
if !unpackAtTop {
|
||
|
code.Print(" }")
|
||
|
}
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" var err error")
|
||
|
code.Print(" x.%s, err = New%s(v%d, compiler.NewContext(\"%s\", context))",
|
||
|
fieldName, typeModel.Name, fieldNumber, propertyName)
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
}
|
||
|
} else if propertyType == "string" {
|
||
|
if propertyModel.Repeated {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" v, ok := v%d.([]interface{})", fieldNumber)
|
||
|
code.Print(" if ok {")
|
||
|
code.Print(" x.%s = compiler.ConvertInterfaceArrayToStringArray(v)", fieldName)
|
||
|
code.Print(" } else {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
|
||
|
if propertyModel.StringEnumValues != nil {
|
||
|
code.Print("// check for valid enum values")
|
||
|
code.Print("// %+v", propertyModel.StringEnumValues)
|
||
|
|
||
|
stringArrayLiteral := "[]string{"
|
||
|
for i, item := range propertyModel.StringEnumValues {
|
||
|
if i > 0 {
|
||
|
stringArrayLiteral += ","
|
||
|
}
|
||
|
stringArrayLiteral += "\"" + item + "\""
|
||
|
}
|
||
|
stringArrayLiteral += "}"
|
||
|
code.Print("if ok && !compiler.StringArrayContainsValues(%s, x.%s) {", stringArrayLiteral, fieldName)
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v\", v%d)", propertyName, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" x.%s, ok = v%d.(string)", fieldName, fieldNumber)
|
||
|
code.Print(" if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print(" }")
|
||
|
|
||
|
if propertyModel.StringEnumValues != nil {
|
||
|
code.Print("// check for valid enum values")
|
||
|
code.Print("// %+v", propertyModel.StringEnumValues)
|
||
|
|
||
|
stringArrayLiteral := "[]string{"
|
||
|
for i, item := range propertyModel.StringEnumValues {
|
||
|
if i > 0 {
|
||
|
stringArrayLiteral += ","
|
||
|
}
|
||
|
stringArrayLiteral += "\"" + item + "\""
|
||
|
}
|
||
|
stringArrayLiteral += "}"
|
||
|
|
||
|
code.Print("if ok && !compiler.StringArrayContainsValue(%s, x.%s) {", stringArrayLiteral, fieldName)
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
code.Print("}")
|
||
|
}
|
||
|
} else if propertyType == "float" {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" switch v%d := v%d.(type) {", fieldNumber, fieldNumber)
|
||
|
code.Print(" case float64:")
|
||
|
code.Print(" x.%s = v%d", fieldName, fieldNumber)
|
||
|
code.Print(" case float32:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" case uint64:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" case uint32:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" case int64:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" case int32:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" case int:")
|
||
|
code.Print(" x.%s = float64(v%d)", fieldName, fieldNumber)
|
||
|
code.Print(" default:")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
} else if propertyType == "int64" {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" t, ok := v%d.(int)", fieldNumber)
|
||
|
code.Print(" if ok {")
|
||
|
code.Print(" x.%s = int64(t)", fieldName)
|
||
|
code.Print(" } else {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
} else if propertyType == "bool" {
|
||
|
if oneOfWrapper {
|
||
|
propertyName := "Boolean"
|
||
|
code.Print("boolValue, ok := in.(bool)")
|
||
|
code.Print("if ok {")
|
||
|
code.Print(" x.Oneof = &%s_%s{%s: boolValue}", parentTypeName, propertyName, propertyName)
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("v%d := compiler.MapValueForKey(m, \"%s\")", fieldNumber, propertyName)
|
||
|
code.Print("if (v%d != nil) {", fieldNumber)
|
||
|
code.Print(" x.%s, ok = v%d.(bool)", fieldName, fieldNumber)
|
||
|
code.Print(" if !ok {")
|
||
|
code.Print(" message := fmt.Sprintf(\"has unexpected value for %s: %%+v (%%T)\", v%d, v%d)", propertyName, fieldNumber, fieldNumber)
|
||
|
code.Print(" errors = append(errors, compiler.NewError(context, message))")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
} else {
|
||
|
mapTypeName := propertyModel.MapType
|
||
|
if mapTypeName != "" {
|
||
|
code.Print("// MAP: %s %s", mapTypeName, propertyModel.Pattern)
|
||
|
if mapTypeName == "string" {
|
||
|
code.Print("x.%s = make([]*NamedString, 0)", fieldName)
|
||
|
} else {
|
||
|
code.Print("x.%s = make([]*Named%s, 0)", fieldName, mapTypeName)
|
||
|
}
|
||
|
code.Print("for _, item := range m {")
|
||
|
code.Print("k, ok := compiler.StringValue(item.Key)")
|
||
|
code.Print("if ok {")
|
||
|
code.Print("v := item.Value")
|
||
|
if pattern := propertyModel.Pattern; pattern != "" {
|
||
|
if inline, ok := regexPatterns.SpecialCaseExpression(pattern, "k"); ok {
|
||
|
code.Print("if %s {", inline)
|
||
|
} else {
|
||
|
code.Print("if %s.MatchString(k) {", nameForPattern(regexPatterns, pattern))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
code.Print("pair := &Named" + strings.Title(mapTypeName) + "{}")
|
||
|
code.Print("pair.Name = k")
|
||
|
|
||
|
if mapTypeName == "string" {
|
||
|
code.Print("pair.Value = v.(string)")
|
||
|
} else if mapTypeName == "Any" {
|
||
|
code.Print("result := &Any{}")
|
||
|
code.Print("handled, resultFromExt, err := compiler.HandleExtension(context, v, k)")
|
||
|
code.Print("if handled {")
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" } else {")
|
||
|
code.Print(" bytes, _ := yaml.Marshal(v)")
|
||
|
code.Print(" result.Yaml = string(bytes)")
|
||
|
code.Print(" result.Value = resultFromExt")
|
||
|
code.Print(" pair.Value = result")
|
||
|
code.Print(" }")
|
||
|
code.Print("} else {")
|
||
|
code.Print(" pair.Value, err = NewAny(v, compiler.NewContext(k, context))")
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
|
||
|
} else {
|
||
|
code.Print("var err error")
|
||
|
code.Print("pair.Value, err = New%s(v, compiler.NewContext(k, context))", mapTypeName)
|
||
|
code.Print("if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
code.Print("x.%s = append(x.%s, pair)", fieldName, fieldName)
|
||
|
if propertyModel.Pattern != "" {
|
||
|
code.Print("}")
|
||
|
}
|
||
|
code.Print("}")
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("// TODO: %s", propertyType)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if unpackAtTop {
|
||
|
code.Print("}")
|
||
|
}
|
||
|
if oneOfWrapper {
|
||
|
code.Print("if matched {")
|
||
|
code.Print(" // since the oneof matched one of its possibilities, discard any matching errors")
|
||
|
code.Print(" errors = make([]error, 0)")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// assumes that the return value is in a variable named "x"
|
||
|
code.Print(" return x, compiler.NewErrorGroupOrNil(errors)")
|
||
|
code.Print("}\n")
|
||
|
}
|
||
|
|
||
|
// ResolveReferences() methods
|
||
|
func (domain *Domain) generateResolveReferencesMethodsForType(code *printer.Code, typeName string) {
|
||
|
code.Print("// ResolveReferences resolves references found inside %s objects.", typeName)
|
||
|
code.Print("func (m *%s) ResolveReferences(root string) (interface{}, error) {", typeName)
|
||
|
code.Print("errors := make([]error, 0)")
|
||
|
|
||
|
typeModel := domain.TypeModels[typeName]
|
||
|
if typeModel.OneOfWrapper {
|
||
|
// call ResolveReferences on whatever is in the Oneof.
|
||
|
for _, propertyModel := range typeModel.Properties {
|
||
|
propertyType := propertyModel.Type
|
||
|
_, typeFound := domain.TypeModels[propertyType]
|
||
|
if typeFound {
|
||
|
code.Print("{")
|
||
|
code.Print("p, ok := m.Oneof.(*%s_%s)", typeName, propertyType)
|
||
|
code.Print("if ok {")
|
||
|
if propertyType == "JsonReference" { // Special case for OpenAPI
|
||
|
code.Print("info, err := p.%s.ResolveReferences(root)", propertyType)
|
||
|
code.Print("if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print("} else if info != nil {")
|
||
|
code.Print(" n, err := New%s(info, nil)", typeName)
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print(" } else if n != nil {")
|
||
|
code.Print(" *m = *n")
|
||
|
code.Print(" return nil, nil")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("_, err := p.%s.ResolveReferences(root)", propertyType)
|
||
|
code.Print("if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
code.Print("}")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for _, propertyModel := range typeModel.Properties {
|
||
|
propertyName := propertyModel.Name
|
||
|
var displayName = propertyName
|
||
|
if displayName == "$ref" {
|
||
|
displayName = "_ref"
|
||
|
}
|
||
|
if displayName == "$schema" {
|
||
|
displayName = "_schema"
|
||
|
}
|
||
|
displayName = camelCaseToSnakeCase(displayName)
|
||
|
|
||
|
fieldName := strings.Title(propertyName)
|
||
|
if propertyName == "$ref" {
|
||
|
fieldName = "XRef"
|
||
|
code.Print("if m.XRef != \"\" {")
|
||
|
//code.Print("log.Printf(\"%s reference to resolve %%+v\", m.XRef)", typeName)
|
||
|
code.Print("info, err := compiler.ReadInfoForRef(root, m.XRef)")
|
||
|
|
||
|
code.Print("if err != nil {")
|
||
|
code.Print(" return nil, err")
|
||
|
code.Print("}")
|
||
|
//code.Print("log.Printf(\"%%+v\", info)")
|
||
|
|
||
|
if len(typeModel.Properties) > 1 {
|
||
|
code.Print("if info != nil {")
|
||
|
code.Print(" replacement, err := New%s(info, nil)", typeName)
|
||
|
code.Print(" if err == nil {")
|
||
|
code.Print(" *m = *replacement")
|
||
|
code.Print(" return m.ResolveReferences(root)")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
code.Print("return info, nil")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
if !propertyModel.Repeated {
|
||
|
propertyType := propertyModel.Type
|
||
|
typeModel, typeFound := domain.TypeModels[propertyType]
|
||
|
if typeFound && !typeModel.IsPair {
|
||
|
code.Print("if m.%s != nil {", fieldName)
|
||
|
code.Print(" _, err := m.%s.ResolveReferences(root)", fieldName)
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
} else {
|
||
|
propertyType := propertyModel.Type
|
||
|
_, typeFound := domain.TypeModels[propertyType]
|
||
|
if typeFound {
|
||
|
code.Print("for _, item := range m.%s {", fieldName)
|
||
|
code.Print("if item != nil {")
|
||
|
code.Print(" _, err := item.ResolveReferences(root)")
|
||
|
code.Print(" if err != nil {")
|
||
|
code.Print(" errors = append(errors, err)")
|
||
|
code.Print(" }")
|
||
|
code.Print("}")
|
||
|
code.Print("}")
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
code.Print(" return nil, compiler.NewErrorGroupOrNil(errors)")
|
||
|
code.Print("}\n")
|
||
|
}
|
||
|
|
||
|
// ToRawInfo() methods
|
||
|
func (domain *Domain) generateToRawInfoMethodForType(code *printer.Code, typeName string) {
|
||
|
code.Print("// ToRawInfo returns a description of %s suitable for JSON or YAML export.", typeName)
|
||
|
code.Print("func (m *%s) ToRawInfo() interface{} {", typeName)
|
||
|
typeModel := domain.TypeModels[typeName]
|
||
|
if typeName == "Any" {
|
||
|
code.Print("var err error")
|
||
|
code.Print("var info1 []yaml.MapSlice")
|
||
|
code.Print("err = yaml.Unmarshal([]byte(m.Yaml), &info1)")
|
||
|
code.Print("if err == nil {return info1}")
|
||
|
code.Print("var info2 yaml.MapSlice")
|
||
|
code.Print("err = yaml.Unmarshal([]byte(m.Yaml), &info2)")
|
||
|
code.Print("if err == nil {return info2}")
|
||
|
code.Print("var info3 interface{}")
|
||
|
code.Print("err = yaml.Unmarshal([]byte(m.Yaml), &info3)")
|
||
|
code.Print("if err == nil {return info3}")
|
||
|
code.Print("return nil")
|
||
|
} else if typeName == "StringArray" {
|
||
|
code.Print("return m.Value")
|
||
|
} else if typeModel.OneOfWrapper {
|
||
|
code.Print("// ONE OF WRAPPER")
|
||
|
code.Print("// %s", typeModel.Name)
|
||
|
for i, item := range typeModel.Properties {
|
||
|
code.Print("// %+v", *item)
|
||
|
if item.Type == "float" {
|
||
|
code.Print("if v%d, ok := m.GetOneof().(*%s_Number); ok {", i, typeName)
|
||
|
code.Print("return v%d.Number", i)
|
||
|
code.Print("}")
|
||
|
} else if item.Type == "bool" {
|
||
|
code.Print("if v%d, ok := m.GetOneof().(*%s_Boolean); ok {", i, typeName)
|
||
|
code.Print("return v%d.Boolean", i)
|
||
|
code.Print("}")
|
||
|
} else if item.Type == "string" {
|
||
|
code.Print("if v%d, ok := m.GetOneof().(*%s_String_); ok {", i, typeName)
|
||
|
code.Print("return v%d.String_", i)
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("v%d := m.Get%s()", i, item.Type)
|
||
|
code.Print("if v%d != nil {", i)
|
||
|
code.Print(" return v%d.ToRawInfo()", i)
|
||
|
code.Print("}")
|
||
|
}
|
||
|
}
|
||
|
code.Print("return nil")
|
||
|
} else {
|
||
|
code.Print("info := yaml.MapSlice{}")
|
||
|
for _, propertyModel := range typeModel.Properties {
|
||
|
switch propertyModel.Type {
|
||
|
case "string":
|
||
|
propertyName := propertyModel.Name
|
||
|
if !propertyModel.Repeated {
|
||
|
code.Print("if m.%s != \"\" {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
}
|
||
|
case "bool":
|
||
|
propertyName := propertyModel.Name
|
||
|
if !propertyModel.Repeated {
|
||
|
code.Print("if m.%s != false {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
}
|
||
|
case "int":
|
||
|
propertyName := propertyModel.Name
|
||
|
if !propertyModel.Repeated {
|
||
|
code.Print("if m.%s != 0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
}
|
||
|
case "float":
|
||
|
propertyName := propertyModel.Name
|
||
|
if !propertyModel.Repeated {
|
||
|
code.Print("if m.%s != 0.0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
} else {
|
||
|
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s})", propertyName, propertyModel.FieldName())
|
||
|
code.Print("}")
|
||
|
}
|
||
|
default:
|
||
|
propertyName := propertyModel.Name
|
||
|
if propertyName == "value" {
|
||
|
code.Print("// %+v", propertyModel)
|
||
|
} else if !propertyModel.Repeated {
|
||
|
code.Print("if m.%s != nil {", propertyModel.FieldName())
|
||
|
if propertyModel.Type == "TypeItem" {
|
||
|
code.Print("if len(m.Type.Value) == 1 {")
|
||
|
code.Print("info = append(info, yaml.MapItem{\"type\", m.Type.Value[0]})")
|
||
|
code.Print("} else {")
|
||
|
code.Print("info = append(info, yaml.MapItem{\"type\", m.Type.Value})")
|
||
|
code.Print("}")
|
||
|
} else if propertyModel.Type == "ItemsItem" {
|
||
|
code.Print("items := make([]interface{}, 0)")
|
||
|
if domain.Version == "v2" {
|
||
|
code.Print("for _, item := range m.Items.Schema {")
|
||
|
} else {
|
||
|
code.Print("for _, item := range m.Items.SchemaOrReference {")
|
||
|
}
|
||
|
code.Print(" items = append(items, item.ToRawInfo())")
|
||
|
code.Print("}")
|
||
|
code.Print("info = append(info, yaml.MapItem{\"items\", items[0]})")
|
||
|
} else {
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", m.%s.ToRawInfo()})",
|
||
|
propertyName, propertyModel.FieldName())
|
||
|
}
|
||
|
code.Print("}")
|
||
|
code.Print("// %+v", propertyModel)
|
||
|
} else if propertyModel.MapType == "string" {
|
||
|
code.Print("// %+v", propertyModel)
|
||
|
} else if propertyModel.MapType != "" {
|
||
|
code.Print("if m.%s != nil {", propertyModel.FieldName())
|
||
|
code.Print("for _, item := range m.%s {", propertyModel.FieldName())
|
||
|
code.Print("info = append(info, yaml.MapItem{item.Name, item.Value.ToRawInfo()})")
|
||
|
code.Print("}")
|
||
|
code.Print("}")
|
||
|
code.Print("// %+v", propertyModel)
|
||
|
} else {
|
||
|
code.Print("if len(m.%s) != 0 {", propertyModel.FieldName())
|
||
|
code.Print("items := make([]interface{}, 0)")
|
||
|
code.Print("for _, item := range m.%s {", propertyModel.FieldName())
|
||
|
code.Print("items = append(items, item.ToRawInfo())")
|
||
|
code.Print("}")
|
||
|
code.Print("info = append(info, yaml.MapItem{\"%s\", items})", propertyName)
|
||
|
code.Print("}")
|
||
|
code.Print("// %+v", propertyModel)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
code.Print("return info")
|
||
|
}
|
||
|
code.Print("}\n")
|
||
|
}
|
||
|
|
||
|
func (domain *Domain) generateConstantVariables(code *printer.Code, regexPatterns *patternNames) {
|
||
|
names := regexPatterns.Names()
|
||
|
var sortedNames []string
|
||
|
for name, _ := range names {
|
||
|
sortedNames = append(sortedNames, name)
|
||
|
}
|
||
|
sort.Strings(sortedNames)
|
||
|
code.Print("var (")
|
||
|
for _, name := range sortedNames {
|
||
|
code.Print("%s = regexp.MustCompile(\"%s\")", name, escapeSlashes(names[name]))
|
||
|
}
|
||
|
code.Print(")\n")
|
||
|
}
|