xref: /aosp_15_r20/build/soong/starlark_fmt/format.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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