github.com/avenga/couper@v1.12.2/internal/seetie/convert.go (about)

     1  package seetie
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"net/http"
     7  	"net/url"
     8  	"regexp"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/zclconf/go-cty/cty"
    14  )
    15  
    16  var validKey = regexp.MustCompile("[a-zA-Z_][a-zA-Z0-9_-]*")
    17  
    18  func ValueToMap(val cty.Value) map[string]interface{} {
    19  	result := make(map[string]interface{})
    20  	if val.IsNull() || !val.IsKnown() {
    21  		return result
    22  	}
    23  	var valMap map[string]cty.Value
    24  	if isListOrTuple(val) {
    25  		valMap = val.AsValueSlice()[0].AsValueMap()
    26  	} else {
    27  		valMap = val.AsValueMap()
    28  	}
    29  
    30  	for k, v := range valMap {
    31  		if v.IsNull() || !v.IsWhollyKnown() {
    32  			result[k] = nil
    33  			continue
    34  		}
    35  		t := v.Type()
    36  		switch t {
    37  		case cty.Bool:
    38  			result[k] = v.True()
    39  		case cty.String:
    40  			result[k] = v.AsString()
    41  		case cty.List(cty.String):
    42  			result[k] = ValueToStringSlice(v)
    43  		case cty.Number:
    44  			f, _ := v.AsBigFloat().Float64()
    45  			result[k] = f
    46  		case cty.Map(cty.NilType):
    47  			result[k] = nil
    48  		default:
    49  			if t.IsObjectType() {
    50  				result[k] = ValueToMap(v)
    51  				continue
    52  			}
    53  			if isListOrTuple(v) {
    54  				result[k] = ValueToStringSlice(v)
    55  				continue
    56  			}
    57  			result[k] = nil
    58  		}
    59  	}
    60  	return result
    61  }
    62  
    63  func ValueToPermission(val cty.Value) (string, map[string]string, error) {
    64  	switch val.Type() {
    65  	case cty.NilType:
    66  		return "", nil, nil
    67  	case cty.String:
    68  		return val.AsString(), nil, nil
    69  	default:
    70  		if val.Type().IsObjectType() {
    71  			permissionMap := make(map[string]string)
    72  			for k, v := range val.AsValueMap() {
    73  				if v.Type() != cty.String {
    74  					return "", nil, fmt.Errorf("unsupported value for method %q in required_permission", k)
    75  				}
    76  				permissionMap[strings.ToUpper(k)] = v.AsString()
    77  			}
    78  			return "", permissionMap, nil
    79  		}
    80  	}
    81  	return "", nil, fmt.Errorf("unsupported value for required_permission")
    82  }
    83  
    84  func ValuesMapToValue(m url.Values) cty.Value {
    85  	result := make(map[string]interface{})
    86  	for k, v := range m {
    87  		result[k] = v
    88  	}
    89  	return MapToValue(result)
    90  }
    91  
    92  func stringListToValue(l []string) cty.Value {
    93  	if len(l) == 0 {
    94  		return cty.ListValEmpty(cty.String)
    95  	}
    96  
    97  	var list []cty.Value
    98  	for _, s := range l {
    99  		list = append(list, cty.StringVal(s))
   100  	}
   101  	return cty.ListVal(list)
   102  }
   103  
   104  func listToValue(l []interface{}) cty.Value {
   105  	var list []cty.Value
   106  	for _, v := range l {
   107  		list = append(list, GoToValue(v))
   108  	}
   109  	return cty.TupleVal(list)
   110  }
   111  
   112  func GoToValue(v interface{}) cty.Value {
   113  	switch v := v.(type) {
   114  	case string:
   115  		return cty.StringVal(v)
   116  	case bool:
   117  		return cty.BoolVal(v)
   118  	case int64:
   119  		return cty.NumberIntVal(v)
   120  	case float64:
   121  		return cty.NumberFloatVal(v)
   122  	case []string:
   123  		return stringListToValue(v)
   124  	case []interface{}:
   125  		return listToValue(v)
   126  	case map[string]interface{}:
   127  		return MapToValue(v)
   128  	default:
   129  		return cty.NullVal(cty.String)
   130  	}
   131  }
   132  
   133  func MapToValue(m map[string]interface{}) cty.Value {
   134  	if m == nil {
   135  		return cty.MapValEmpty(cty.NilType)
   136  	}
   137  
   138  	ctyMap := make(map[string]cty.Value)
   139  
   140  	for k, v := range m {
   141  		if !validKey.MatchString(k) {
   142  			continue
   143  		}
   144  		switch v := v.(type) {
   145  		case []string:
   146  			ctyMap[k] = stringListToValue(v)
   147  		case []interface{}:
   148  			ctyMap[k] = listToValue(v)
   149  		case map[string]interface{}:
   150  			ctyMap[k] = MapToValue(v)
   151  		default:
   152  			ctyMap[k] = GoToValue(v)
   153  		}
   154  	}
   155  
   156  	if len(ctyMap) == 0 {
   157  		return cty.MapValEmpty(cty.NilType) // prevent attribute access on nil values
   158  	}
   159  
   160  	return cty.ObjectVal(ctyMap)
   161  }
   162  
   163  func HeaderToMapValue(headers http.Header) cty.Value {
   164  	ctyMap := make(map[string]cty.Value)
   165  	for k, v := range headers {
   166  		if validKey.MatchString(k) {
   167  			if len(v) == 0 {
   168  				ctyMap[strings.ToLower(k)] = cty.StringVal("")
   169  				continue
   170  			}
   171  			ctyMap[strings.ToLower(k)] = cty.StringVal(v[0]) // TODO: ListVal??
   172  		}
   173  	}
   174  	if len(ctyMap) == 0 {
   175  		return cty.MapValEmpty(cty.String)
   176  	}
   177  	return cty.MapVal(ctyMap)
   178  }
   179  
   180  func CookiesToMapValue(cookies []*http.Cookie) cty.Value {
   181  	ctyMap := make(map[string]cty.Value)
   182  	for _, cookie := range cookies {
   183  		ctyMap[cookie.Name] = cty.StringVal(cookie.Value) // TODO: ListVal??
   184  	}
   185  
   186  	if len(ctyMap) == 0 {
   187  		return cty.MapValEmpty(cty.String)
   188  	}
   189  	return cty.MapVal(ctyMap)
   190  }
   191  
   192  func ValueToStringSlice(src cty.Value) []string {
   193  	var l []string
   194  	if !src.IsKnown() || src.IsNull() {
   195  		return l
   196  	}
   197  
   198  	switch src.Type() {
   199  	case cty.NilType:
   200  		return l
   201  	case cty.Bool, cty.Number, cty.String:
   202  		return append(l, ValueToString(src))
   203  	default:
   204  		for _, s := range src.AsValueSlice() {
   205  			if !s.IsKnown() {
   206  				continue
   207  			}
   208  			l = append(l, ValueToString(s))
   209  		}
   210  	}
   211  	return l
   212  }
   213  
   214  func ValueToIntSlice(src cty.Value) []int64 {
   215  	var n []int64
   216  	if !src.IsKnown() || src.IsNull() || !src.CanIterateElements() {
   217  		return n
   218  	}
   219  
   220  	for _, s := range src.AsValueSlice() {
   221  		if !s.IsKnown() {
   222  			continue
   223  		}
   224  		n = append(n, ValueToInt(s))
   225  	}
   226  	return n
   227  }
   228  
   229  var whitespaceRegex = regexp.MustCompile(`^\s*$`)
   230  
   231  // ValueToString explicitly drops all other (unknown) types and
   232  // converts non whitespace strings or numbers to its string representation.
   233  func ValueToString(v cty.Value) string {
   234  	if v.IsNull() || !v.IsKnown() {
   235  		return ""
   236  	}
   237  
   238  	switch v.Type() {
   239  	case cty.String:
   240  		str := v.AsString()
   241  		if whitespaceRegex.MatchString(str) {
   242  			return ""
   243  		}
   244  		return str
   245  	case cty.Number:
   246  		n := v.AsBigFloat()
   247  		ni, accuracy := n.Int(nil)
   248  		if accuracy == big.Exact {
   249  			return ni.String()
   250  		}
   251  		return n.String()
   252  	default:
   253  		return ""
   254  	}
   255  }
   256  
   257  func ValueToInt(v cty.Value) (n int64) {
   258  	if !v.IsWhollyKnown() {
   259  		return n
   260  	}
   261  
   262  	switch v.Type() {
   263  	case cty.String:
   264  		i, err := strconv.Atoi(v.AsString())
   265  		if err == nil {
   266  			n = int64(i)
   267  		}
   268  	case cty.Number:
   269  		bn := v.AsBigFloat()
   270  		n, _ = bn.Int64()
   271  	}
   272  
   273  	return n
   274  }
   275  
   276  func ValueToLogFields(val cty.Value) logrus.Fields {
   277  	if val.IsNull() || !val.IsKnown() {
   278  		return nil
   279  	}
   280  
   281  	fields := logrus.Fields{}
   282  
   283  	for k, v := range val.AsValueMap() {
   284  		if isListOrTuple(v) {
   285  			fields[k] = valueToLogFieldsFromListOrTuple(v)
   286  		} else {
   287  			switch v.Type() {
   288  			case cty.Bool:
   289  				fields[k] = v.True()
   290  			case cty.String:
   291  				fields[k] = v.AsString()
   292  			case cty.Number:
   293  				f, _ := v.AsBigFloat().Float64()
   294  				fields[k] = f
   295  			default:
   296  				if isMapOrObject(v) {
   297  					fields[k] = ValueToLogFields(v)
   298  				}
   299  			}
   300  		}
   301  	}
   302  
   303  	return fields
   304  }
   305  
   306  func valueToLogFieldsFromListOrTuple(val cty.Value) []interface{} {
   307  	if !isListOrTuple(val) {
   308  		return nil
   309  	}
   310  
   311  	var values []interface{}
   312  	for _, v := range val.AsValueSlice() {
   313  		if isListOrTuple(v) {
   314  			values = append(values, valueToLogFieldsFromListOrTuple(v))
   315  		} else {
   316  			switch v.Type() {
   317  			case cty.Bool:
   318  				values = append(values, v.True())
   319  			case cty.String:
   320  				values = append(values, v.AsString())
   321  			case cty.Number:
   322  				f, _ := v.AsBigFloat().Float64()
   323  				values = append(values, f)
   324  			default:
   325  				if isMapOrObject(v) {
   326  					values = append(values, ValueToLogFields(v))
   327  				}
   328  			}
   329  		}
   330  	}
   331  
   332  	return values
   333  }
   334  
   335  func SliceToString(sl []interface{}) string {
   336  	var str []string
   337  	for _, s := range sl {
   338  		if result := ToString(s); result != "" {
   339  			str = append(str, result)
   340  		}
   341  	}
   342  	return strings.Join(str, ",")
   343  }
   344  
   345  func ToString(s interface{}) string {
   346  	switch s := s.(type) {
   347  	case []string:
   348  		return strings.Join(s, ",")
   349  	case []interface{}:
   350  		return SliceToString(s)
   351  	case string:
   352  		return s
   353  	case int:
   354  		return strconv.Itoa(s)
   355  	case float64:
   356  		return fmt.Sprintf("%0.f", s)
   357  	case bool:
   358  		if !s {
   359  			return "false"
   360  		}
   361  		return "true"
   362  	default:
   363  		return ""
   364  	}
   365  }
   366  
   367  func isMapOrObject(v cty.Value) bool {
   368  	if v.IsNull() {
   369  		return false
   370  	}
   371  	t := v.Type()
   372  	return t.IsMapType() || t.IsObjectType()
   373  }
   374  
   375  func isListOrTuple(v cty.Value) bool {
   376  	if v.IsNull() {
   377  		return false
   378  	}
   379  	t := v.Type()
   380  	return t.IsListType() || t.IsTupleType()
   381  }