github.com/bazelbuild/bazel-gazelle@v0.36.1-0.20240520142334-61b277ba6fed/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  func (g GlobValue) BzlExpr() bzl.Expr {
    41  	patternsValue := ExprFromValue(g.Patterns)
    42  	globArgs := []bzl.Expr{patternsValue}
    43  	if len(g.Excludes) > 0 {
    44  		excludesValue := ExprFromValue(g.Excludes)
    45  		globArgs = append(globArgs, &bzl.AssignExpr{
    46  			LHS: &bzl.LiteralExpr{Token: "exclude"},
    47  			Op:  "=",
    48  			RHS: excludesValue,
    49  		})
    50  	}
    51  	return &bzl.CallExpr{
    52  		X:    &bzl.LiteralExpr{Token: "glob"},
    53  		List: globArgs,
    54  	}
    55  }
    56  
    57  // BzlExprValue is implemented by types that have custom translations
    58  // to Starlark values.
    59  type BzlExprValue interface {
    60  	BzlExpr() bzl.Expr
    61  }
    62  
    63  // Merger is implemented by types that can merge their data into an
    64  // existing Starlark expression.
    65  //
    66  // When Merge is invoked, it is responsible for returning a Starlark expression that contains the
    67  // result of merging its data into the previously-existing expression provided as other.
    68  // Note that other can be nil, if no previous attr with this name existed.
    69  type Merger interface {
    70  	Merge(other bzl.Expr) bzl.Expr
    71  }
    72  
    73  type SortedStrings []string
    74  
    75  func (s SortedStrings) BzlExpr() bzl.Expr {
    76  	list := make([]bzl.Expr, len(s))
    77  	for i, v := range s {
    78  		list[i] = &bzl.StringExpr{Value: v}
    79  	}
    80  	listExpr := &bzl.ListExpr{List: list}
    81  	sortExprLabels(listExpr, []bzl.Expr{})
    82  	return listExpr
    83  }
    84  
    85  func (s SortedStrings) Merge(other bzl.Expr) bzl.Expr {
    86  	if other == nil {
    87  		return s.BzlExpr()
    88  	}
    89  	merged := MergeList(s.BzlExpr(), other)
    90  	sortExprLabels(merged, []bzl.Expr{})
    91  	return merged
    92  }
    93  
    94  type UnsortedStrings []string
    95  
    96  func (s UnsortedStrings) Merge(other bzl.Expr) bzl.Expr {
    97  	if other == nil {
    98  		return ExprFromValue(s)
    99  	}
   100  	return MergeList(ExprFromValue(s), other)
   101  }
   102  
   103  // SelectStringListValue is a value that can be translated to a Bazel
   104  // select expression that picks a string list based on a string condition.
   105  type SelectStringListValue map[string][]string
   106  
   107  func (s SelectStringListValue) BzlExpr() bzl.Expr {
   108  	defaultKey := "//conditions:default"
   109  	keys := make([]string, 0, len(s))
   110  	haveDefaultKey := false
   111  	for key := range s {
   112  		if key == defaultKey {
   113  			haveDefaultKey = true
   114  		} else {
   115  			keys = append(keys, key)
   116  		}
   117  	}
   118  	sort.Strings(keys)
   119  	if haveDefaultKey {
   120  		keys = append(keys, defaultKey)
   121  	}
   122  
   123  	args := make([]*bzl.KeyValueExpr, 0, len(s))
   124  	for _, key := range keys {
   125  		value := ExprFromValue(s[key])
   126  		if key != defaultKey {
   127  			value.(*bzl.ListExpr).ForceMultiLine = true
   128  		}
   129  		args = append(args, &bzl.KeyValueExpr{
   130  			Key:   &bzl.StringExpr{Value: key},
   131  			Value: value,
   132  		})
   133  	}
   134  	sel := &bzl.CallExpr{
   135  		X:    &bzl.Ident{Name: "select"},
   136  		List: []bzl.Expr{&bzl.DictExpr{List: args, ForceMultiLine: true}},
   137  	}
   138  	return sel
   139  }
   140  
   141  // ExprFromValue converts a value into an expression that can be written into
   142  // a Bazel build file. The following types of values can be converted:
   143  //
   144  //   * bools, integers, floats, strings.
   145  //   * labels (converted to strings).
   146  //   * slices, arrays (converted to lists).
   147  //   * maps (converted to select expressions; keys must be rules in
   148  //     @io_bazel_rules_go//go/platform).
   149  //   * GlobValue (converted to glob expressions).
   150  //   * PlatformStrings (converted to a concatenation of a list and selects).
   151  //
   152  // Converting unsupported types will cause a panic.
   153  func ExprFromValue(val interface{}) bzl.Expr {
   154  	if e, ok := val.(bzl.Expr); ok {
   155  		return e
   156  	}
   157  	if be, ok := val.(BzlExprValue); ok {
   158  		return be.BzlExpr()
   159  	}
   160  
   161  	rv := reflect.ValueOf(val)
   162  	switch rv.Kind() {
   163  	case reflect.Bool:
   164  		tok := "False"
   165  		if rv.Bool() {
   166  			tok = "True"
   167  		}
   168  		return &bzl.LiteralExpr{Token: tok}
   169  
   170  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   171  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   172  		return &bzl.LiteralExpr{Token: fmt.Sprintf("%d", val)}
   173  
   174  	case reflect.Float32, reflect.Float64:
   175  		return &bzl.LiteralExpr{Token: fmt.Sprintf("%f", val)}
   176  
   177  	case reflect.String:
   178  		return &bzl.StringExpr{Value: val.(string)}
   179  
   180  	case reflect.Slice, reflect.Array:
   181  		var list []bzl.Expr
   182  		for i := 0; i < rv.Len(); i++ {
   183  			elem := ExprFromValue(rv.Index(i).Interface())
   184  			list = append(list, elem)
   185  		}
   186  		return &bzl.ListExpr{List: list}
   187  
   188  	case reflect.Map:
   189  		rkeys := rv.MapKeys()
   190  		sort.Sort(byString(rkeys))
   191  		args := make([]*bzl.KeyValueExpr, len(rkeys))
   192  		for i, rk := range rkeys {
   193  			k := &bzl.StringExpr{Value: mapKeyString(rk)}
   194  			v := ExprFromValue(rv.MapIndex(rk).Interface())
   195  			if l, ok := v.(*bzl.ListExpr); ok {
   196  				l.ForceMultiLine = true
   197  			}
   198  			args[i] = &bzl.KeyValueExpr{Key: k, Value: v}
   199  		}
   200  		return &bzl.DictExpr{List: args, ForceMultiLine: true}
   201  	}
   202  
   203  	log.Panicf("type not supported: %T", val)
   204  	return nil
   205  }
   206  
   207  func mapKeyString(k reflect.Value) string {
   208  	switch s := k.Interface().(type) {
   209  	case string:
   210  		return s
   211  	default:
   212  		log.Panicf("unexpected map key: %v", k)
   213  		return ""
   214  	}
   215  }
   216  
   217  type byString []reflect.Value
   218  
   219  var _ sort.Interface = byString{}
   220  
   221  func (s byString) Len() int {
   222  	return len(s)
   223  }
   224  
   225  func (s byString) Less(i, j int) bool {
   226  	return mapKeyString(s[i]) < mapKeyString(s[j])
   227  }
   228  
   229  func (s byString) Swap(i, j int) {
   230  	s[i], s[j] = s[j], s[i]
   231  }