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 }