github.com/wolfd/bazel-gazelle@v0.14.0/internal/rule/value.go (about)

     1  /* Copyright 2016 The Bazel Authors. 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  
    16  package rule
    17  
    18  import (
    19  	"fmt"
    20  	"log"
    21  	"reflect"
    22  	"sort"
    23  
    24  	bzl "github.com/bazelbuild/buildtools/build"
    25  )
    26  
    27  // KeyValue represents a key-value pair. This gets converted into a
    28  // rule attribute, i.e., a Skylark keyword argument.
    29  type KeyValue struct {
    30  	Key   string
    31  	Value interface{}
    32  }
    33  
    34  // GlobValue represents a Bazel glob expression.
    35  type GlobValue struct {
    36  	Patterns []string
    37  	Excludes []string
    38  }
    39  
    40  // ExprFromValue converts a value into an expression that can be written into
    41  // a Bazel build file. The following types of values can be converted:
    42  //
    43  // * bools, integers, floats, strings.
    44  // * slices, arrays (converted to lists).
    45  // * maps (converted to select expressions; keys must be rules in
    46  //   @io_bazel_rules_go//go/platform).
    47  // * GlobValue (converted to glob expressions).
    48  // * PlatformStrings (converted to a concatenation of a list and selects).
    49  //
    50  // Converting unsupported types will cause a panic.
    51  func ExprFromValue(val interface{}) bzl.Expr {
    52  	if e, ok := val.(bzl.Expr); ok {
    53  		return e
    54  	}
    55  
    56  	rv := reflect.ValueOf(val)
    57  	switch rv.Kind() {
    58  	case reflect.Bool:
    59  		tok := "False"
    60  		if rv.Bool() {
    61  			tok = "True"
    62  		}
    63  		return &bzl.LiteralExpr{Token: tok}
    64  
    65  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
    66  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
    67  		return &bzl.LiteralExpr{Token: fmt.Sprintf("%d", val)}
    68  
    69  	case reflect.Float32, reflect.Float64:
    70  		return &bzl.LiteralExpr{Token: fmt.Sprintf("%f", val)}
    71  
    72  	case reflect.String:
    73  		return &bzl.StringExpr{Value: val.(string)}
    74  
    75  	case reflect.Slice, reflect.Array:
    76  		var list []bzl.Expr
    77  		for i := 0; i < rv.Len(); i++ {
    78  			elem := ExprFromValue(rv.Index(i).Interface())
    79  			list = append(list, elem)
    80  		}
    81  		return &bzl.ListExpr{List: list}
    82  
    83  	case reflect.Map:
    84  		rkeys := rv.MapKeys()
    85  		sort.Sort(byString(rkeys))
    86  		args := make([]bzl.Expr, len(rkeys))
    87  		for i, rk := range rkeys {
    88  			label := fmt.Sprintf("@io_bazel_rules_go//go/platform:%s", mapKeyString(rk))
    89  			k := &bzl.StringExpr{Value: label}
    90  			v := ExprFromValue(rv.MapIndex(rk).Interface())
    91  			if l, ok := v.(*bzl.ListExpr); ok {
    92  				l.ForceMultiLine = true
    93  			}
    94  			args[i] = &bzl.KeyValueExpr{Key: k, Value: v}
    95  		}
    96  		args = append(args, &bzl.KeyValueExpr{
    97  			Key:   &bzl.StringExpr{Value: "//conditions:default"},
    98  			Value: &bzl.ListExpr{},
    99  		})
   100  		sel := &bzl.CallExpr{
   101  			X:    &bzl.LiteralExpr{Token: "select"},
   102  			List: []bzl.Expr{&bzl.DictExpr{List: args, ForceMultiLine: true}},
   103  		}
   104  		return sel
   105  
   106  	case reflect.Struct:
   107  		switch val := val.(type) {
   108  		case GlobValue:
   109  			patternsValue := ExprFromValue(val.Patterns)
   110  			globArgs := []bzl.Expr{patternsValue}
   111  			if len(val.Excludes) > 0 {
   112  				excludesValue := ExprFromValue(val.Excludes)
   113  				globArgs = append(globArgs, &bzl.KeyValueExpr{
   114  					Key:   &bzl.StringExpr{Value: "excludes"},
   115  					Value: excludesValue,
   116  				})
   117  			}
   118  			return &bzl.CallExpr{
   119  				X:    &bzl.LiteralExpr{Token: "glob"},
   120  				List: globArgs,
   121  			}
   122  
   123  		case PlatformStrings:
   124  			var pieces []bzl.Expr
   125  			if len(val.Generic) > 0 {
   126  				pieces = append(pieces, ExprFromValue(val.Generic))
   127  			}
   128  			if len(val.OS) > 0 {
   129  				pieces = append(pieces, ExprFromValue(val.OS))
   130  			}
   131  			if len(val.Arch) > 0 {
   132  				pieces = append(pieces, ExprFromValue(val.Arch))
   133  			}
   134  			if len(val.Platform) > 0 {
   135  				pieces = append(pieces, ExprFromValue(val.Platform))
   136  			}
   137  			if len(pieces) == 0 {
   138  				return &bzl.ListExpr{}
   139  			} else if len(pieces) == 1 {
   140  				return pieces[0]
   141  			} else {
   142  				e := pieces[0]
   143  				if list, ok := e.(*bzl.ListExpr); ok {
   144  					list.ForceMultiLine = true
   145  				}
   146  				for _, piece := range pieces[1:] {
   147  					e = &bzl.BinaryExpr{X: e, Y: piece, Op: "+"}
   148  				}
   149  				return e
   150  			}
   151  		}
   152  	}
   153  
   154  	log.Panicf("type not supported: %T", val)
   155  	return nil
   156  }
   157  
   158  func mapKeyString(k reflect.Value) string {
   159  	switch s := k.Interface().(type) {
   160  	case string:
   161  		return s
   162  	case Platform:
   163  		return s.String()
   164  	default:
   165  		log.Panicf("unexpected map key: %v", k)
   166  		return ""
   167  	}
   168  }
   169  
   170  type byString []reflect.Value
   171  
   172  var _ sort.Interface = byString{}
   173  
   174  func (s byString) Len() int {
   175  	return len(s)
   176  }
   177  
   178  func (s byString) Less(i, j int) bool {
   179  	return mapKeyString(s[i]) < mapKeyString(s[j])
   180  }
   181  
   182  func (s byString) Swap(i, j int) {
   183  	s[i], s[j] = s[j], s[i]
   184  }