2022-04-23 09:01:19 +00:00
|
|
|
// Copyright 2017 Google LLC. All Rights Reserved.
|
2019-01-18 01:50:10 +00:00
|
|
|
//
|
|
|
|
// 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 compiler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
2022-04-23 09:01:19 +00:00
|
|
|
|
|
|
|
"github.com/googleapis/gnostic/jsonschema"
|
|
|
|
"gopkg.in/yaml.v3"
|
2019-01-18 01:50:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// compiler helper functions, usually called from generated code
|
|
|
|
|
2022-04-23 09:01:19 +00:00
|
|
|
// UnpackMap gets a *yaml.Node if possible.
|
|
|
|
func UnpackMap(in *yaml.Node) (*yaml.Node, bool) {
|
|
|
|
if in == nil {
|
|
|
|
return nil, false
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
2022-04-23 09:01:19 +00:00
|
|
|
return in, true
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
|
2022-04-23 09:01:19 +00:00
|
|
|
// SortedKeysForMap returns the sorted keys of a yamlv2.MapSlice.
|
|
|
|
func SortedKeysForMap(m *yaml.Node) []string {
|
2019-01-18 01:50:10 +00:00
|
|
|
keys := make([]string, 0)
|
2022-04-23 09:01:19 +00:00
|
|
|
if m.Kind == yaml.MappingNode {
|
|
|
|
for i := 0; i < len(m.Content); i += 2 {
|
|
|
|
keys = append(keys, m.Content[i].Value)
|
|
|
|
}
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
2022-04-23 09:01:19 +00:00
|
|
|
// MapHasKey returns true if a yamlv2.MapSlice contains a specified key.
|
|
|
|
func MapHasKey(m *yaml.Node, key string) bool {
|
|
|
|
if m == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if m.Kind == yaml.MappingNode {
|
|
|
|
for i := 0; i < len(m.Content); i += 2 {
|
|
|
|
itemKey := m.Content[i].Value
|
|
|
|
if key == itemKey {
|
|
|
|
return true
|
|
|
|
}
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// MapValueForKey gets the value of a map value for a specified key.
|
2022-04-23 09:01:19 +00:00
|
|
|
func MapValueForKey(m *yaml.Node, key string) *yaml.Node {
|
|
|
|
if m == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if m.Kind == yaml.MappingNode {
|
|
|
|
for i := 0; i < len(m.Content); i += 2 {
|
|
|
|
itemKey := m.Content[i].Value
|
|
|
|
if key == itemKey {
|
|
|
|
return m.Content[i+1]
|
|
|
|
}
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConvertInterfaceArrayToStringArray converts an array of interfaces to an array of strings, if possible.
|
|
|
|
func ConvertInterfaceArrayToStringArray(interfaceArray []interface{}) []string {
|
|
|
|
stringArray := make([]string, 0)
|
|
|
|
for _, item := range interfaceArray {
|
|
|
|
v, ok := item.(string)
|
|
|
|
if ok {
|
|
|
|
stringArray = append(stringArray, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stringArray
|
|
|
|
}
|
|
|
|
|
2022-04-23 09:01:19 +00:00
|
|
|
// SequenceNodeForNode returns a node if it is a SequenceNode.
|
|
|
|
func SequenceNodeForNode(node *yaml.Node) (*yaml.Node, bool) {
|
|
|
|
if node.Kind != yaml.SequenceNode {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return node, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoolForScalarNode returns the bool value of a node.
|
|
|
|
func BoolForScalarNode(node *yaml.Node) (bool, bool) {
|
|
|
|
if node == nil {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
if node.Kind == yaml.DocumentNode {
|
|
|
|
return BoolForScalarNode(node.Content[0])
|
|
|
|
}
|
|
|
|
if node.Kind != yaml.ScalarNode {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
if node.Tag != "!!bool" {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
v, err := strconv.ParseBool(node.Value)
|
|
|
|
if err != nil {
|
|
|
|
return false, false
|
|
|
|
}
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// IntForScalarNode returns the integer value of a node.
|
|
|
|
func IntForScalarNode(node *yaml.Node) (int64, bool) {
|
|
|
|
if node == nil {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
if node.Kind == yaml.DocumentNode {
|
|
|
|
return IntForScalarNode(node.Content[0])
|
|
|
|
}
|
|
|
|
if node.Kind != yaml.ScalarNode {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
if node.Tag != "!!int" {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
v, err := strconv.ParseInt(node.Value, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// FloatForScalarNode returns the float value of a node.
|
|
|
|
func FloatForScalarNode(node *yaml.Node) (float64, bool) {
|
|
|
|
if node == nil {
|
|
|
|
return 0.0, false
|
|
|
|
}
|
|
|
|
if node.Kind == yaml.DocumentNode {
|
|
|
|
return FloatForScalarNode(node.Content[0])
|
|
|
|
}
|
|
|
|
if node.Kind != yaml.ScalarNode {
|
|
|
|
return 0.0, false
|
|
|
|
}
|
|
|
|
if (node.Tag != "!!int") && (node.Tag != "!!float") {
|
|
|
|
return 0.0, false
|
|
|
|
}
|
|
|
|
v, err := strconv.ParseFloat(node.Value, 64)
|
|
|
|
if err != nil {
|
|
|
|
return 0.0, false
|
|
|
|
}
|
|
|
|
return v, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringForScalarNode returns the string value of a node.
|
|
|
|
func StringForScalarNode(node *yaml.Node) (string, bool) {
|
|
|
|
if node == nil {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
if node.Kind == yaml.DocumentNode {
|
|
|
|
return StringForScalarNode(node.Content[0])
|
|
|
|
}
|
|
|
|
switch node.Kind {
|
|
|
|
case yaml.ScalarNode:
|
|
|
|
switch node.Tag {
|
|
|
|
case "!!int":
|
|
|
|
return node.Value, true
|
|
|
|
case "!!str":
|
|
|
|
return node.Value, true
|
|
|
|
case "!!timestamp":
|
|
|
|
return node.Value, true
|
|
|
|
case "!!null":
|
|
|
|
return "", true
|
|
|
|
default:
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringArrayForSequenceNode converts a sequence node to an array of strings, if possible.
|
|
|
|
func StringArrayForSequenceNode(node *yaml.Node) []string {
|
|
|
|
stringArray := make([]string, 0)
|
|
|
|
for _, item := range node.Content {
|
|
|
|
v, ok := StringForScalarNode(item)
|
|
|
|
if ok {
|
|
|
|
stringArray = append(stringArray, v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return stringArray
|
|
|
|
}
|
|
|
|
|
2019-01-18 01:50:10 +00:00
|
|
|
// MissingKeysInMap identifies which keys from a list of required keys are not in a map.
|
2022-04-23 09:01:19 +00:00
|
|
|
func MissingKeysInMap(m *yaml.Node, requiredKeys []string) []string {
|
2019-01-18 01:50:10 +00:00
|
|
|
missingKeys := make([]string, 0)
|
|
|
|
for _, k := range requiredKeys {
|
|
|
|
if !MapHasKey(m, k) {
|
|
|
|
missingKeys = append(missingKeys, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return missingKeys
|
|
|
|
}
|
|
|
|
|
|
|
|
// InvalidKeysInMap returns keys in a map that don't match a list of allowed keys and patterns.
|
2022-04-23 09:01:19 +00:00
|
|
|
func InvalidKeysInMap(m *yaml.Node, allowedKeys []string, allowedPatterns []*regexp.Regexp) []string {
|
2019-01-18 01:50:10 +00:00
|
|
|
invalidKeys := make([]string, 0)
|
2022-04-23 09:01:19 +00:00
|
|
|
if m == nil || m.Kind != yaml.MappingNode {
|
|
|
|
return invalidKeys
|
|
|
|
}
|
|
|
|
for i := 0; i < len(m.Content); i += 2 {
|
|
|
|
key := m.Content[i].Value
|
|
|
|
found := false
|
|
|
|
// does the key match an allowed key?
|
|
|
|
for _, allowedKey := range allowedKeys {
|
|
|
|
if key == allowedKey {
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
// does the key match an allowed pattern?
|
|
|
|
for _, allowedPattern := range allowedPatterns {
|
|
|
|
if allowedPattern.MatchString(key) {
|
2019-01-18 01:50:10 +00:00
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2022-04-23 09:01:19 +00:00
|
|
|
invalidKeys = append(invalidKeys, key)
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return invalidKeys
|
|
|
|
}
|
|
|
|
|
2022-04-23 09:01:19 +00:00
|
|
|
// NewNullNode creates a new Null node.
|
|
|
|
func NewNullNode() *yaml.Node {
|
|
|
|
node := &yaml.Node{
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Tag: "!!null",
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
2022-04-23 09:01:19 +00:00
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMappingNode creates a new Mapping node.
|
|
|
|
func NewMappingNode() *yaml.Node {
|
|
|
|
return &yaml.Node{
|
|
|
|
Kind: yaml.MappingNode,
|
|
|
|
Content: make([]*yaml.Node, 0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewSequenceNode creates a new Sequence node.
|
|
|
|
func NewSequenceNode() *yaml.Node {
|
|
|
|
node := &yaml.Node{
|
|
|
|
Kind: yaml.SequenceNode,
|
|
|
|
Content: make([]*yaml.Node, 0),
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScalarNodeForString creates a new node to hold a string.
|
|
|
|
func NewScalarNodeForString(s string) *yaml.Node {
|
|
|
|
return &yaml.Node{
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Tag: "!!str",
|
|
|
|
Value: s,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewSequenceNodeForStringArray creates a new node to hold an array of strings.
|
|
|
|
func NewSequenceNodeForStringArray(strings []string) *yaml.Node {
|
|
|
|
node := &yaml.Node{
|
|
|
|
Kind: yaml.SequenceNode,
|
|
|
|
Content: make([]*yaml.Node, 0),
|
|
|
|
}
|
|
|
|
for _, s := range strings {
|
|
|
|
node.Content = append(node.Content, NewScalarNodeForString(s))
|
|
|
|
}
|
|
|
|
return node
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScalarNodeForBool creates a new node to hold a bool.
|
|
|
|
func NewScalarNodeForBool(b bool) *yaml.Node {
|
|
|
|
return &yaml.Node{
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Tag: "!!bool",
|
|
|
|
Value: fmt.Sprintf("%t", b),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScalarNodeForFloat creates a new node to hold a float.
|
|
|
|
func NewScalarNodeForFloat(f float64) *yaml.Node {
|
|
|
|
return &yaml.Node{
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Tag: "!!float",
|
|
|
|
Value: fmt.Sprintf("%g", f),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewScalarNodeForInt creates a new node to hold an integer.
|
|
|
|
func NewScalarNodeForInt(i int64) *yaml.Node {
|
|
|
|
return &yaml.Node{
|
|
|
|
Kind: yaml.ScalarNode,
|
|
|
|
Tag: "!!int",
|
|
|
|
Value: fmt.Sprintf("%d", i),
|
2019-01-18 01:50:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PluralProperties returns the string "properties" pluralized.
|
|
|
|
func PluralProperties(count int) string {
|
|
|
|
if count == 1 {
|
|
|
|
return "property"
|
|
|
|
}
|
|
|
|
return "properties"
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringArrayContainsValue returns true if a string array contains a specified value.
|
|
|
|
func StringArrayContainsValue(array []string, value string) bool {
|
|
|
|
for _, item := range array {
|
|
|
|
if item == value {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringArrayContainsValues returns true if a string array contains all of a list of specified values.
|
|
|
|
func StringArrayContainsValues(array []string, values []string) bool {
|
|
|
|
for _, value := range values {
|
|
|
|
if !StringArrayContainsValue(array, value) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// StringValue returns the string value of an item.
|
|
|
|
func StringValue(item interface{}) (value string, ok bool) {
|
|
|
|
value, ok = item.(string)
|
|
|
|
if ok {
|
|
|
|
return value, ok
|
|
|
|
}
|
|
|
|
intValue, ok := item.(int)
|
|
|
|
if ok {
|
|
|
|
return strconv.Itoa(intValue), true
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|
2022-04-23 09:01:19 +00:00
|
|
|
|
|
|
|
// Description returns a human-readable represention of an item.
|
|
|
|
func Description(item interface{}) string {
|
|
|
|
value, ok := item.(*yaml.Node)
|
|
|
|
if ok {
|
|
|
|
return jsonschema.Render(value)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%+v", item)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display returns a description of a node for use in error messages.
|
|
|
|
func Display(node *yaml.Node) string {
|
|
|
|
switch node.Kind {
|
|
|
|
case yaml.ScalarNode:
|
|
|
|
switch node.Tag {
|
|
|
|
case "!!str":
|
|
|
|
return fmt.Sprintf("%s (string)", node.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%+v (%T)", node, node)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal creates a yaml version of a structure in our preferred style
|
|
|
|
func Marshal(in *yaml.Node) []byte {
|
|
|
|
clearStyle(in)
|
|
|
|
//bytes, _ := yaml.Marshal(&yaml.Node{Kind: yaml.DocumentNode, Content: []*yaml.Node{in}})
|
|
|
|
bytes, _ := yaml.Marshal(in)
|
|
|
|
|
|
|
|
return bytes
|
|
|
|
}
|
|
|
|
|
|
|
|
func clearStyle(node *yaml.Node) {
|
|
|
|
node.Style = 0
|
|
|
|
for _, c := range node.Content {
|
|
|
|
clearStyle(c)
|
|
|
|
}
|
|
|
|
}
|