...

Source file src/github.com/google/gnostic-models/compiler/helpers.go

Documentation: github.com/google/gnostic-models/compiler

     1  // Copyright 2017 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package compiler
    16  
    17  import (
    18  	"fmt"
    19  	"regexp"
    20  	"sort"
    21  	"strconv"
    22  
    23  	"gopkg.in/yaml.v3"
    24  
    25  	"github.com/google/gnostic-models/jsonschema"
    26  )
    27  
    28  // compiler helper functions, usually called from generated code
    29  
    30  // UnpackMap gets a *yaml.Node if possible.
    31  func UnpackMap(in *yaml.Node) (*yaml.Node, bool) {
    32  	if in == nil {
    33  		return nil, false
    34  	}
    35  	return in, true
    36  }
    37  
    38  // SortedKeysForMap returns the sorted keys of a yamlv2.MapSlice.
    39  func SortedKeysForMap(m *yaml.Node) []string {
    40  	keys := make([]string, 0)
    41  	if m.Kind == yaml.MappingNode {
    42  		for i := 0; i < len(m.Content); i += 2 {
    43  			keys = append(keys, m.Content[i].Value)
    44  		}
    45  	}
    46  	sort.Strings(keys)
    47  	return keys
    48  }
    49  
    50  // MapHasKey returns true if a yamlv2.MapSlice contains a specified key.
    51  func MapHasKey(m *yaml.Node, key string) bool {
    52  	if m == nil {
    53  		return false
    54  	}
    55  	if m.Kind == yaml.MappingNode {
    56  		for i := 0; i < len(m.Content); i += 2 {
    57  			itemKey := m.Content[i].Value
    58  			if key == itemKey {
    59  				return true
    60  			}
    61  		}
    62  	}
    63  	return false
    64  }
    65  
    66  // MapValueForKey gets the value of a map value for a specified key.
    67  func MapValueForKey(m *yaml.Node, key string) *yaml.Node {
    68  	if m == nil {
    69  		return nil
    70  	}
    71  	if m.Kind == yaml.MappingNode {
    72  		for i := 0; i < len(m.Content); i += 2 {
    73  			itemKey := m.Content[i].Value
    74  			if key == itemKey {
    75  				return m.Content[i+1]
    76  			}
    77  		}
    78  	}
    79  	return nil
    80  }
    81  
    82  // ConvertInterfaceArrayToStringArray converts an array of interfaces to an array of strings, if possible.
    83  func ConvertInterfaceArrayToStringArray(interfaceArray []interface{}) []string {
    84  	stringArray := make([]string, 0)
    85  	for _, item := range interfaceArray {
    86  		v, ok := item.(string)
    87  		if ok {
    88  			stringArray = append(stringArray, v)
    89  		}
    90  	}
    91  	return stringArray
    92  }
    93  
    94  // SequenceNodeForNode returns a node if it is a SequenceNode.
    95  func SequenceNodeForNode(node *yaml.Node) (*yaml.Node, bool) {
    96  	if node.Kind != yaml.SequenceNode {
    97  		return nil, false
    98  	}
    99  	return node, true
   100  }
   101  
   102  // BoolForScalarNode returns the bool value of a node.
   103  func BoolForScalarNode(node *yaml.Node) (bool, bool) {
   104  	if node == nil {
   105  		return false, false
   106  	}
   107  	if node.Kind == yaml.DocumentNode {
   108  		return BoolForScalarNode(node.Content[0])
   109  	}
   110  	if node.Kind != yaml.ScalarNode {
   111  		return false, false
   112  	}
   113  	if node.Tag != "!!bool" {
   114  		return false, false
   115  	}
   116  	v, err := strconv.ParseBool(node.Value)
   117  	if err != nil {
   118  		return false, false
   119  	}
   120  	return v, true
   121  }
   122  
   123  // IntForScalarNode returns the integer value of a node.
   124  func IntForScalarNode(node *yaml.Node) (int64, bool) {
   125  	if node == nil {
   126  		return 0, false
   127  	}
   128  	if node.Kind == yaml.DocumentNode {
   129  		return IntForScalarNode(node.Content[0])
   130  	}
   131  	if node.Kind != yaml.ScalarNode {
   132  		return 0, false
   133  	}
   134  	if node.Tag != "!!int" {
   135  		return 0, false
   136  	}
   137  	v, err := strconv.ParseInt(node.Value, 10, 64)
   138  	if err != nil {
   139  		return 0, false
   140  	}
   141  	return v, true
   142  }
   143  
   144  // FloatForScalarNode returns the float value of a node.
   145  func FloatForScalarNode(node *yaml.Node) (float64, bool) {
   146  	if node == nil {
   147  		return 0.0, false
   148  	}
   149  	if node.Kind == yaml.DocumentNode {
   150  		return FloatForScalarNode(node.Content[0])
   151  	}
   152  	if node.Kind != yaml.ScalarNode {
   153  		return 0.0, false
   154  	}
   155  	if (node.Tag != "!!int") && (node.Tag != "!!float") {
   156  		return 0.0, false
   157  	}
   158  	v, err := strconv.ParseFloat(node.Value, 64)
   159  	if err != nil {
   160  		return 0.0, false
   161  	}
   162  	return v, true
   163  }
   164  
   165  // StringForScalarNode returns the string value of a node.
   166  func StringForScalarNode(node *yaml.Node) (string, bool) {
   167  	if node == nil {
   168  		return "", false
   169  	}
   170  	if node.Kind == yaml.DocumentNode {
   171  		return StringForScalarNode(node.Content[0])
   172  	}
   173  	switch node.Kind {
   174  	case yaml.ScalarNode:
   175  		switch node.Tag {
   176  		case "!!int":
   177  			return node.Value, true
   178  		case "!!str":
   179  			return node.Value, true
   180  		case "!!timestamp":
   181  			return node.Value, true
   182  		case "!!null":
   183  			return "", true
   184  		default:
   185  			return "", false
   186  		}
   187  	default:
   188  		return "", false
   189  	}
   190  }
   191  
   192  // StringArrayForSequenceNode converts a sequence node to an array of strings, if possible.
   193  func StringArrayForSequenceNode(node *yaml.Node) []string {
   194  	stringArray := make([]string, 0)
   195  	for _, item := range node.Content {
   196  		v, ok := StringForScalarNode(item)
   197  		if ok {
   198  			stringArray = append(stringArray, v)
   199  		}
   200  	}
   201  	return stringArray
   202  }
   203  
   204  // MissingKeysInMap identifies which keys from a list of required keys are not in a map.
   205  func MissingKeysInMap(m *yaml.Node, requiredKeys []string) []string {
   206  	missingKeys := make([]string, 0)
   207  	for _, k := range requiredKeys {
   208  		if !MapHasKey(m, k) {
   209  			missingKeys = append(missingKeys, k)
   210  		}
   211  	}
   212  	return missingKeys
   213  }
   214  
   215  // InvalidKeysInMap returns keys in a map that don't match a list of allowed keys and patterns.
   216  func InvalidKeysInMap(m *yaml.Node, allowedKeys []string, allowedPatterns []*regexp.Regexp) []string {
   217  	invalidKeys := make([]string, 0)
   218  	if m == nil || m.Kind != yaml.MappingNode {
   219  		return invalidKeys
   220  	}
   221  	for i := 0; i < len(m.Content); i += 2 {
   222  		key := m.Content[i].Value
   223  		found := false
   224  		// does the key match an allowed key?
   225  		for _, allowedKey := range allowedKeys {
   226  			if key == allowedKey {
   227  				found = true
   228  				break
   229  			}
   230  		}
   231  		if !found {
   232  			// does the key match an allowed pattern?
   233  			for _, allowedPattern := range allowedPatterns {
   234  				if allowedPattern.MatchString(key) {
   235  					found = true
   236  					break
   237  				}
   238  			}
   239  			if !found {
   240  				invalidKeys = append(invalidKeys, key)
   241  			}
   242  		}
   243  	}
   244  	return invalidKeys
   245  }
   246  
   247  // NewNullNode creates a new Null node.
   248  func NewNullNode() *yaml.Node {
   249  	node := &yaml.Node{
   250  		Kind: yaml.ScalarNode,
   251  		Tag:  "!!null",
   252  	}
   253  	return node
   254  }
   255  
   256  // NewMappingNode creates a new Mapping node.
   257  func NewMappingNode() *yaml.Node {
   258  	return &yaml.Node{
   259  		Kind:    yaml.MappingNode,
   260  		Content: make([]*yaml.Node, 0),
   261  	}
   262  }
   263  
   264  // NewSequenceNode creates a new Sequence node.
   265  func NewSequenceNode() *yaml.Node {
   266  	node := &yaml.Node{
   267  		Kind:    yaml.SequenceNode,
   268  		Content: make([]*yaml.Node, 0),
   269  	}
   270  	return node
   271  }
   272  
   273  // NewScalarNodeForString creates a new node to hold a string.
   274  func NewScalarNodeForString(s string) *yaml.Node {
   275  	return &yaml.Node{
   276  		Kind:  yaml.ScalarNode,
   277  		Tag:   "!!str",
   278  		Value: s,
   279  	}
   280  }
   281  
   282  // NewSequenceNodeForStringArray creates a new node to hold an array of strings.
   283  func NewSequenceNodeForStringArray(strings []string) *yaml.Node {
   284  	node := &yaml.Node{
   285  		Kind:    yaml.SequenceNode,
   286  		Content: make([]*yaml.Node, 0),
   287  	}
   288  	for _, s := range strings {
   289  		node.Content = append(node.Content, NewScalarNodeForString(s))
   290  	}
   291  	return node
   292  }
   293  
   294  // NewScalarNodeForBool creates a new node to hold a bool.
   295  func NewScalarNodeForBool(b bool) *yaml.Node {
   296  	return &yaml.Node{
   297  		Kind:  yaml.ScalarNode,
   298  		Tag:   "!!bool",
   299  		Value: fmt.Sprintf("%t", b),
   300  	}
   301  }
   302  
   303  // NewScalarNodeForFloat creates a new node to hold a float.
   304  func NewScalarNodeForFloat(f float64) *yaml.Node {
   305  	return &yaml.Node{
   306  		Kind:  yaml.ScalarNode,
   307  		Tag:   "!!float",
   308  		Value: fmt.Sprintf("%g", f),
   309  	}
   310  }
   311  
   312  // NewScalarNodeForInt creates a new node to hold an integer.
   313  func NewScalarNodeForInt(i int64) *yaml.Node {
   314  	return &yaml.Node{
   315  		Kind:  yaml.ScalarNode,
   316  		Tag:   "!!int",
   317  		Value: fmt.Sprintf("%d", i),
   318  	}
   319  }
   320  
   321  // PluralProperties returns the string "properties" pluralized.
   322  func PluralProperties(count int) string {
   323  	if count == 1 {
   324  		return "property"
   325  	}
   326  	return "properties"
   327  }
   328  
   329  // StringArrayContainsValue returns true if a string array contains a specified value.
   330  func StringArrayContainsValue(array []string, value string) bool {
   331  	for _, item := range array {
   332  		if item == value {
   333  			return true
   334  		}
   335  	}
   336  	return false
   337  }
   338  
   339  // StringArrayContainsValues returns true if a string array contains all of a list of specified values.
   340  func StringArrayContainsValues(array []string, values []string) bool {
   341  	for _, value := range values {
   342  		if !StringArrayContainsValue(array, value) {
   343  			return false
   344  		}
   345  	}
   346  	return true
   347  }
   348  
   349  // StringValue returns the string value of an item.
   350  func StringValue(item interface{}) (value string, ok bool) {
   351  	value, ok = item.(string)
   352  	if ok {
   353  		return value, ok
   354  	}
   355  	intValue, ok := item.(int)
   356  	if ok {
   357  		return strconv.Itoa(intValue), true
   358  	}
   359  	return "", false
   360  }
   361  
   362  // Description returns a human-readable represention of an item.
   363  func Description(item interface{}) string {
   364  	value, ok := item.(*yaml.Node)
   365  	if ok {
   366  		return jsonschema.Render(value)
   367  	}
   368  	return fmt.Sprintf("%+v", item)
   369  }
   370  
   371  // Display returns a description of a node for use in error messages.
   372  func Display(node *yaml.Node) string {
   373  	switch node.Kind {
   374  	case yaml.ScalarNode:
   375  		switch node.Tag {
   376  		case "!!str":
   377  			return fmt.Sprintf("%s (string)", node.Value)
   378  		}
   379  	}
   380  	return fmt.Sprintf("%+v (%T)", node, node)
   381  }
   382  
   383  // Marshal creates a yaml version of a structure in our preferred style
   384  func Marshal(in *yaml.Node) []byte {
   385  	clearStyle(in)
   386  	//bytes, _ := yaml.Marshal(&yaml.Node{Kind: yaml.DocumentNode, Content: []*yaml.Node{in}})
   387  	bytes, _ := yaml.Marshal(in)
   388  
   389  	return bytes
   390  }
   391  
   392  func clearStyle(node *yaml.Node) {
   393  	node.Style = 0
   394  	for _, c := range node.Content {
   395  		clearStyle(c)
   396  	}
   397  }
   398  

View as plain text