github.imxd.top/operator-framework/operator-sdk@v0.8.2/pkg/ansible/paramconv/paramconv.go (about)

     1  // Copyright 2018 The Operator-SDK Authors
     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  // Based on https://github.com/iancoleman/strcase
    16  
    17  package paramconv
    18  
    19  import (
    20  	"regexp"
    21  	"strings"
    22  )
    23  
    24  var (
    25  	numberSequence    = regexp.MustCompile(`([a-zA-Z])(\d+)([a-zA-Z]?)`)
    26  	numberReplacement = []byte(`$1 $2 $3`)
    27  	wordMapping       = map[string]string{
    28  		"http": "HTTP",
    29  		"url":  "URL",
    30  		"ip":   "IP",
    31  	}
    32  )
    33  
    34  func addWordBoundariesToNumbers(s string) string {
    35  	b := []byte(s)
    36  	b = numberSequence.ReplaceAll(b, numberReplacement)
    37  	return string(b)
    38  }
    39  
    40  func translateWord(word string, initCase bool) string {
    41  	if val, ok := wordMapping[word]; ok {
    42  		return val
    43  	}
    44  	if initCase {
    45  		return strings.Title(word)
    46  	}
    47  	return word
    48  }
    49  
    50  // Converts a string to CamelCase
    51  func ToCamel(s string) string {
    52  	s = addWordBoundariesToNumbers(s)
    53  	s = strings.Trim(s, " ")
    54  	n := ""
    55  	bits := []string{}
    56  	for _, v := range s {
    57  		if v == '_' || v == ' ' || v == '-' {
    58  			bits = append(bits, n)
    59  			n = ""
    60  		} else {
    61  			n += string(v)
    62  		}
    63  	}
    64  	bits = append(bits, n)
    65  
    66  	ret := ""
    67  	for i, substr := range bits {
    68  		ret += translateWord(substr, i != 0)
    69  	}
    70  	return ret
    71  }
    72  
    73  // Converts a string to snake_case
    74  func ToSnake(s string) string {
    75  	s = addWordBoundariesToNumbers(s)
    76  	s = strings.Trim(s, " ")
    77  	var prefix string
    78  	char1 := []rune(s)[0]
    79  	if char1 >= 'A' && char1 <= 'Z' {
    80  		prefix = "_"
    81  	} else {
    82  		prefix = ""
    83  	}
    84  	bits := []string{}
    85  	n := ""
    86  	real_i := -1
    87  
    88  	for i, v := range s {
    89  		real_i += 1
    90  		// treat acronyms as words, eg for JSONData -> JSON is a whole word
    91  		nextCaseIsChanged := false
    92  		if i+1 < len(s) {
    93  			next := s[i+1]
    94  			if (v >= 'A' && v <= 'Z' && next >= 'a' && next <= 'z') || (v >= 'a' && v <= 'z' && next >= 'A' && next <= 'Z') {
    95  				nextCaseIsChanged = true
    96  			}
    97  		}
    98  
    99  		if real_i > 0 && n[len(n)-1] != '_' && nextCaseIsChanged {
   100  			// add underscore if next letter case type is changed
   101  			if v >= 'A' && v <= 'Z' {
   102  				bits = append(bits, strings.ToLower(n))
   103  				n = string(v)
   104  				real_i = 0
   105  			} else if v >= 'a' && v <= 'z' {
   106  				bits = append(bits, strings.ToLower(n+string(v)))
   107  				n = ""
   108  				real_i = -1
   109  			}
   110  		} else if v == ' ' || v == '_' || v == '-' {
   111  			// replace spaces/underscores with delimiters
   112  			bits = append(bits, strings.ToLower(n))
   113  			n = ""
   114  			real_i = -1
   115  		} else {
   116  			n = n + string(v)
   117  		}
   118  	}
   119  	bits = append(bits, strings.ToLower(n))
   120  	joined := strings.Join(bits, "_")
   121  	if _, ok := wordMapping[bits[0]]; !ok {
   122  		return prefix + joined
   123  	}
   124  	return joined
   125  }
   126  
   127  func convertParameter(fn func(string) string, v interface{}) interface{} {
   128  	switch v := v.(type) {
   129  	case map[string]interface{}:
   130  		ret := map[string]interface{}{}
   131  		for key, val := range v {
   132  			ret[fn(key)] = convertParameter(fn, val)
   133  		}
   134  		return ret
   135  	case []interface{}:
   136  		return convertArray(fn, v)
   137  	default:
   138  		return v
   139  	}
   140  }
   141  
   142  func convertArray(fn func(string) string, in []interface{}) []interface{} {
   143  	res := make([]interface{}, len(in))
   144  	for i, v := range in {
   145  		res[i] = convertParameter(fn, v)
   146  	}
   147  	return res
   148  }
   149  
   150  func convertMapKeys(fn func(string) string, in map[string]interface{}) map[string]interface{} {
   151  	converted := map[string]interface{}{}
   152  	for key, val := range in {
   153  		converted[fn(key)] = convertParameter(fn, val)
   154  	}
   155  	return converted
   156  }
   157  
   158  func MapToSnake(in map[string]interface{}) map[string]interface{} {
   159  	return convertMapKeys(ToSnake, in)
   160  }
   161  
   162  func MapToCamel(in map[string]interface{}) map[string]interface{} {
   163  	return convertMapKeys(ToCamel, in)
   164  }