1*333d2b36SAndroid Build Coastguard Worker// Copyright 2022 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage starlark_fmt 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "fmt" 19*333d2b36SAndroid Build Coastguard Worker "reflect" 20*333d2b36SAndroid Build Coastguard Worker "sort" 21*333d2b36SAndroid Build Coastguard Worker "strconv" 22*333d2b36SAndroid Build Coastguard Worker "strings" 23*333d2b36SAndroid Build Coastguard Worker) 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Workerconst ( 26*333d2b36SAndroid Build Coastguard Worker indent = 4 27*333d2b36SAndroid Build Coastguard Worker) 28*333d2b36SAndroid Build Coastguard Worker 29*333d2b36SAndroid Build Coastguard Worker// Indention returns an indent string of the specified level. 30*333d2b36SAndroid Build Coastguard Workerfunc Indention(level int) string { 31*333d2b36SAndroid Build Coastguard Worker if level < 0 { 32*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level)) 33*333d2b36SAndroid Build Coastguard Worker } 34*333d2b36SAndroid Build Coastguard Worker return strings.Repeat(" ", level*indent) 35*333d2b36SAndroid Build Coastguard Worker} 36*333d2b36SAndroid Build Coastguard Worker 37*333d2b36SAndroid Build Coastguard Workerfunc PrintAny(value any, indentLevel int) string { 38*333d2b36SAndroid Build Coastguard Worker return printAnyRecursive(reflect.ValueOf(value), indentLevel) 39*333d2b36SAndroid Build Coastguard Worker} 40*333d2b36SAndroid Build Coastguard Worker 41*333d2b36SAndroid Build Coastguard Workerfunc printAnyRecursive(value reflect.Value, indentLevel int) string { 42*333d2b36SAndroid Build Coastguard Worker switch value.Type().Kind() { 43*333d2b36SAndroid Build Coastguard Worker case reflect.String: 44*333d2b36SAndroid Build Coastguard Worker val := value.String() 45*333d2b36SAndroid Build Coastguard Worker if strings.Contains(val, "\"") || strings.Contains(val, "\n") { 46*333d2b36SAndroid Build Coastguard Worker return `'''` + val + `'''` 47*333d2b36SAndroid Build Coastguard Worker } 48*333d2b36SAndroid Build Coastguard Worker return `"` + val + `"` 49*333d2b36SAndroid Build Coastguard Worker case reflect.Bool: 50*333d2b36SAndroid Build Coastguard Worker if value.Bool() { 51*333d2b36SAndroid Build Coastguard Worker return "True" 52*333d2b36SAndroid Build Coastguard Worker } else { 53*333d2b36SAndroid Build Coastguard Worker return "False" 54*333d2b36SAndroid Build Coastguard Worker } 55*333d2b36SAndroid Build Coastguard Worker case reflect.Int: 56*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("%d", value.Int()) 57*333d2b36SAndroid Build Coastguard Worker case reflect.Slice: 58*333d2b36SAndroid Build Coastguard Worker if value.Len() == 0 { 59*333d2b36SAndroid Build Coastguard Worker return "[]" 60*333d2b36SAndroid Build Coastguard Worker } else if value.Len() == 1 { 61*333d2b36SAndroid Build Coastguard Worker return "[" + printAnyRecursive(value.Index(0), indentLevel) + "]" 62*333d2b36SAndroid Build Coastguard Worker } 63*333d2b36SAndroid Build Coastguard Worker list := make([]string, 0, value.Len()+2) 64*333d2b36SAndroid Build Coastguard Worker list = append(list, "[") 65*333d2b36SAndroid Build Coastguard Worker innerIndent := Indention(indentLevel + 1) 66*333d2b36SAndroid Build Coastguard Worker for i := 0; i < value.Len(); i++ { 67*333d2b36SAndroid Build Coastguard Worker list = append(list, innerIndent+printAnyRecursive(value.Index(i), indentLevel+1)+`,`) 68*333d2b36SAndroid Build Coastguard Worker } 69*333d2b36SAndroid Build Coastguard Worker list = append(list, Indention(indentLevel)+"]") 70*333d2b36SAndroid Build Coastguard Worker return strings.Join(list, "\n") 71*333d2b36SAndroid Build Coastguard Worker case reflect.Map: 72*333d2b36SAndroid Build Coastguard Worker if value.Len() == 0 { 73*333d2b36SAndroid Build Coastguard Worker return "{}" 74*333d2b36SAndroid Build Coastguard Worker } 75*333d2b36SAndroid Build Coastguard Worker items := make([]string, 0, value.Len()) 76*333d2b36SAndroid Build Coastguard Worker for _, key := range value.MapKeys() { 77*333d2b36SAndroid Build Coastguard Worker items = append(items, fmt.Sprintf(`%s%s: %s,`, Indention(indentLevel+1), printAnyRecursive(key, indentLevel+1), printAnyRecursive(value.MapIndex(key), indentLevel+1))) 78*333d2b36SAndroid Build Coastguard Worker } 79*333d2b36SAndroid Build Coastguard Worker sort.Strings(items) 80*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf(`{ 81*333d2b36SAndroid Build Coastguard Worker%s 82*333d2b36SAndroid Build Coastguard Worker%s}`, strings.Join(items, "\n"), Indention(indentLevel)) 83*333d2b36SAndroid Build Coastguard Worker case reflect.Struct: 84*333d2b36SAndroid Build Coastguard Worker if value.NumField() == 0 { 85*333d2b36SAndroid Build Coastguard Worker return "struct()" 86*333d2b36SAndroid Build Coastguard Worker } 87*333d2b36SAndroid Build Coastguard Worker items := make([]string, 0, value.NumField()+2) 88*333d2b36SAndroid Build Coastguard Worker items = append(items, "struct(") 89*333d2b36SAndroid Build Coastguard Worker for i := 0; i < value.NumField(); i++ { 90*333d2b36SAndroid Build Coastguard Worker if value.Type().Field(i).Anonymous { 91*333d2b36SAndroid Build Coastguard Worker panic("anonymous fields aren't supported") 92*333d2b36SAndroid Build Coastguard Worker } 93*333d2b36SAndroid Build Coastguard Worker name := value.Type().Field(i).Name 94*333d2b36SAndroid Build Coastguard Worker items = append(items, fmt.Sprintf(`%s%s = %s,`, Indention(indentLevel+1), name, printAnyRecursive(value.Field(i), indentLevel+1))) 95*333d2b36SAndroid Build Coastguard Worker } 96*333d2b36SAndroid Build Coastguard Worker items = append(items, Indention(indentLevel)+")") 97*333d2b36SAndroid Build Coastguard Worker return strings.Join(items, "\n") 98*333d2b36SAndroid Build Coastguard Worker default: 99*333d2b36SAndroid Build Coastguard Worker panic("Unhandled kind: " + value.Kind().String()) 100*333d2b36SAndroid Build Coastguard Worker } 101*333d2b36SAndroid Build Coastguard Worker} 102*333d2b36SAndroid Build Coastguard Worker 103*333d2b36SAndroid Build Coastguard Worker// PrintBool returns a Starlark compatible bool string. 104*333d2b36SAndroid Build Coastguard Workerfunc PrintBool(item bool) string { 105*333d2b36SAndroid Build Coastguard Worker if item { 106*333d2b36SAndroid Build Coastguard Worker return "True" 107*333d2b36SAndroid Build Coastguard Worker } else { 108*333d2b36SAndroid Build Coastguard Worker return "False" 109*333d2b36SAndroid Build Coastguard Worker } 110*333d2b36SAndroid Build Coastguard Worker} 111*333d2b36SAndroid Build Coastguard Worker 112*333d2b36SAndroid Build Coastguard Worker// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels. 113*333d2b36SAndroid Build Coastguard Workerfunc PrintStringList(items []string, indentLevel int) string { 114*333d2b36SAndroid Build Coastguard Worker return PrintList(items, indentLevel, func(s string) string { 115*333d2b36SAndroid Build Coastguard Worker if strings.Contains(s, "\"") { 116*333d2b36SAndroid Build Coastguard Worker return `'''%s'''` 117*333d2b36SAndroid Build Coastguard Worker } 118*333d2b36SAndroid Build Coastguard Worker return `"%s"` 119*333d2b36SAndroid Build Coastguard Worker }) 120*333d2b36SAndroid Build Coastguard Worker} 121*333d2b36SAndroid Build Coastguard Worker 122*333d2b36SAndroid Build Coastguard Worker// PrintList returns a Starlark-compatible string of list formmated as requested. 123*333d2b36SAndroid Build Coastguard Workerfunc PrintList(items []string, indentLevel int, formatString func(string) string) string { 124*333d2b36SAndroid Build Coastguard Worker if len(items) == 0 { 125*333d2b36SAndroid Build Coastguard Worker return "[]" 126*333d2b36SAndroid Build Coastguard Worker } else if len(items) == 1 { 127*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf("["+formatString(items[0])+"]", items[0]) 128*333d2b36SAndroid Build Coastguard Worker } 129*333d2b36SAndroid Build Coastguard Worker list := make([]string, 0, len(items)+2) 130*333d2b36SAndroid Build Coastguard Worker list = append(list, "[") 131*333d2b36SAndroid Build Coastguard Worker innerIndent := Indention(indentLevel + 1) 132*333d2b36SAndroid Build Coastguard Worker for _, item := range items { 133*333d2b36SAndroid Build Coastguard Worker list = append(list, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item)) 134*333d2b36SAndroid Build Coastguard Worker } 135*333d2b36SAndroid Build Coastguard Worker list = append(list, Indention(indentLevel)+"]") 136*333d2b36SAndroid Build Coastguard Worker return strings.Join(list, "\n") 137*333d2b36SAndroid Build Coastguard Worker} 138*333d2b36SAndroid Build Coastguard Worker 139*333d2b36SAndroid Build Coastguard Worker// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with 140*333d2b36SAndroid Build Coastguard Worker// string keys and list of string values. 141*333d2b36SAndroid Build Coastguard Workerfunc PrintStringListDict(dict map[string][]string, indentLevel int) string { 142*333d2b36SAndroid Build Coastguard Worker formattedValueDict := make(map[string]string, len(dict)) 143*333d2b36SAndroid Build Coastguard Worker for k, v := range dict { 144*333d2b36SAndroid Build Coastguard Worker formattedValueDict[k] = PrintStringList(v, indentLevel+1) 145*333d2b36SAndroid Build Coastguard Worker } 146*333d2b36SAndroid Build Coastguard Worker return PrintDict(formattedValueDict, indentLevel) 147*333d2b36SAndroid Build Coastguard Worker} 148*333d2b36SAndroid Build Coastguard Worker 149*333d2b36SAndroid Build Coastguard Worker// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and 150*333d2b36SAndroid Build Coastguard Worker// values printed with no additional formatting. 151*333d2b36SAndroid Build Coastguard Workerfunc PrintBoolDict(dict map[string]bool, indentLevel int) string { 152*333d2b36SAndroid Build Coastguard Worker formattedValueDict := make(map[string]string, len(dict)) 153*333d2b36SAndroid Build Coastguard Worker for k, v := range dict { 154*333d2b36SAndroid Build Coastguard Worker formattedValueDict[k] = PrintBool(v) 155*333d2b36SAndroid Build Coastguard Worker } 156*333d2b36SAndroid Build Coastguard Worker return PrintDict(formattedValueDict, indentLevel) 157*333d2b36SAndroid Build Coastguard Worker} 158*333d2b36SAndroid Build Coastguard Worker 159*333d2b36SAndroid Build Coastguard Worker// PrintStringIntDict returns a Starlark-compatible string formatted as dictionary with 160*333d2b36SAndroid Build Coastguard Worker// string keys and int values. 161*333d2b36SAndroid Build Coastguard Workerfunc PrintStringIntDict(dict map[string]int, indentLevel int) string { 162*333d2b36SAndroid Build Coastguard Worker valDict := make(map[string]string, len(dict)) 163*333d2b36SAndroid Build Coastguard Worker for k, v := range dict { 164*333d2b36SAndroid Build Coastguard Worker valDict[k] = strconv.Itoa(v) 165*333d2b36SAndroid Build Coastguard Worker } 166*333d2b36SAndroid Build Coastguard Worker return PrintDict(valDict, indentLevel) 167*333d2b36SAndroid Build Coastguard Worker} 168*333d2b36SAndroid Build Coastguard Worker 169*333d2b36SAndroid Build Coastguard Worker// PrintStringStringDict returns a Starlark-compatible string formatted as dictionary with 170*333d2b36SAndroid Build Coastguard Worker// string keys and string values. 171*333d2b36SAndroid Build Coastguard Workerfunc PrintStringStringDict(dict map[string]string, indentLevel int) string { 172*333d2b36SAndroid Build Coastguard Worker valDict := make(map[string]string, len(dict)) 173*333d2b36SAndroid Build Coastguard Worker for k, v := range dict { 174*333d2b36SAndroid Build Coastguard Worker valDict[k] = fmt.Sprintf(`"%s"`, v) 175*333d2b36SAndroid Build Coastguard Worker } 176*333d2b36SAndroid Build Coastguard Worker return PrintDict(valDict, indentLevel) 177*333d2b36SAndroid Build Coastguard Worker} 178*333d2b36SAndroid Build Coastguard Worker 179*333d2b36SAndroid Build Coastguard Worker// PrintDict returns a starlark-compatible string containing a dictionary with string keys and 180*333d2b36SAndroid Build Coastguard Worker// values printed with no additional formatting. 181*333d2b36SAndroid Build Coastguard Workerfunc PrintDict(dict map[string]string, indentLevel int) string { 182*333d2b36SAndroid Build Coastguard Worker if len(dict) == 0 { 183*333d2b36SAndroid Build Coastguard Worker return "{}" 184*333d2b36SAndroid Build Coastguard Worker } 185*333d2b36SAndroid Build Coastguard Worker items := make([]string, 0, len(dict)) 186*333d2b36SAndroid Build Coastguard Worker for k, v := range dict { 187*333d2b36SAndroid Build Coastguard Worker items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v)) 188*333d2b36SAndroid Build Coastguard Worker } 189*333d2b36SAndroid Build Coastguard Worker sort.Strings(items) 190*333d2b36SAndroid Build Coastguard Worker return fmt.Sprintf(`{ 191*333d2b36SAndroid Build Coastguard Worker%s 192*333d2b36SAndroid Build Coastguard Worker%s}`, strings.Join(items, "\n"), Indention(indentLevel)) 193*333d2b36SAndroid Build Coastguard Worker} 194