blob: 0224bcf931c400493369d6c7a44177496080ffcd [file] [log] [blame]
Liz Kammer72beb342022-02-03 08:42:10 -05001// Copyright 2022 Google Inc. 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
15package starlark_fmt
16
17import (
18 "fmt"
Cole Faust87c0c332023-07-31 12:10:12 -070019 "reflect"
Liz Kammer72beb342022-02-03 08:42:10 -050020 "sort"
Alix Espino4fd7e742023-02-24 14:46:43 +000021 "strconv"
Liz Kammer72beb342022-02-03 08:42:10 -050022 "strings"
23)
24
25const (
26 indent = 4
27)
28
29// Indention returns an indent string of the specified level.
30func Indention(level int) string {
31 if level < 0 {
32 panic(fmt.Errorf("indent level cannot be less than 0, but got %d", level))
33 }
34 return strings.Repeat(" ", level*indent)
35}
36
Cole Faust87c0c332023-07-31 12:10:12 -070037func PrintAny(value any, indentLevel int) string {
38 return printAnyRecursive(reflect.ValueOf(value), indentLevel)
39}
40
41func printAnyRecursive(value reflect.Value, indentLevel int) string {
42 switch value.Type().Kind() {
43 case reflect.String:
44 val := value.String()
45 if strings.Contains(val, "\"") || strings.Contains(val, "\n") {
46 return `'''` + val + `'''`
47 }
48 return `"` + val + `"`
49 case reflect.Bool:
50 if value.Bool() {
51 return "True"
52 } else {
53 return "False"
54 }
55 case reflect.Int:
56 return fmt.Sprintf("%d", value.Int())
57 case reflect.Slice:
58 if value.Len() == 0 {
59 return "[]"
60 } else if value.Len() == 1 {
61 return "[" + printAnyRecursive(value.Index(0), indentLevel) + "]"
62 }
63 list := make([]string, 0, value.Len()+2)
64 list = append(list, "[")
65 innerIndent := Indention(indentLevel + 1)
66 for i := 0; i < value.Len(); i++ {
67 list = append(list, innerIndent+printAnyRecursive(value.Index(i), indentLevel+1)+`,`)
68 }
69 list = append(list, Indention(indentLevel)+"]")
70 return strings.Join(list, "\n")
71 case reflect.Map:
72 if value.Len() == 0 {
73 return "{}"
74 }
75 items := make([]string, 0, value.Len())
76 for _, key := range value.MapKeys() {
77 items = append(items, fmt.Sprintf(`%s%s: %s,`, Indention(indentLevel+1), printAnyRecursive(key, indentLevel+1), printAnyRecursive(value.MapIndex(key), indentLevel+1)))
78 }
79 sort.Strings(items)
80 return fmt.Sprintf(`{
81%s
82%s}`, strings.Join(items, "\n"), Indention(indentLevel))
83 case reflect.Struct:
84 if value.NumField() == 0 {
85 return "struct()"
86 }
87 items := make([]string, 0, value.NumField()+2)
88 items = append(items, "struct(")
89 for i := 0; i < value.NumField(); i++ {
90 if value.Type().Field(i).Anonymous {
91 panic("anonymous fields aren't supported")
92 }
93 name := value.Type().Field(i).Name
94 items = append(items, fmt.Sprintf(`%s%s = %s,`, Indention(indentLevel+1), name, printAnyRecursive(value.Field(i), indentLevel+1)))
95 }
96 items = append(items, Indention(indentLevel)+")")
97 return strings.Join(items, "\n")
98 default:
99 panic("Unhandled kind: " + value.Kind().String())
100 }
101}
102
Liz Kammer72beb342022-02-03 08:42:10 -0500103// PrintBool returns a Starlark compatible bool string.
104func PrintBool(item bool) string {
Cole Fausteb644cf2023-04-11 13:48:17 -0700105 if item {
106 return "True"
107 } else {
108 return "False"
109 }
Liz Kammer72beb342022-02-03 08:42:10 -0500110}
111
112// PrintsStringList returns a Starlark-compatible string of a list of Strings/Labels.
113func PrintStringList(items []string, indentLevel int) string {
Sam Delmerico932c01c2022-03-25 16:33:26 +0000114 return PrintList(items, indentLevel, func(s string) string {
115 if strings.Contains(s, "\"") {
116 return `'''%s'''`
117 }
118 return `"%s"`
119 })
Liz Kammer72beb342022-02-03 08:42:10 -0500120}
121
122// PrintList returns a Starlark-compatible string of list formmated as requested.
Sam Delmerico932c01c2022-03-25 16:33:26 +0000123func PrintList(items []string, indentLevel int, formatString func(string) string) string {
Liz Kammer72beb342022-02-03 08:42:10 -0500124 if len(items) == 0 {
125 return "[]"
126 } else if len(items) == 1 {
Sam Delmerico932c01c2022-03-25 16:33:26 +0000127 return fmt.Sprintf("["+formatString(items[0])+"]", items[0])
Liz Kammer72beb342022-02-03 08:42:10 -0500128 }
129 list := make([]string, 0, len(items)+2)
130 list = append(list, "[")
131 innerIndent := Indention(indentLevel + 1)
132 for _, item := range items {
Sam Delmerico932c01c2022-03-25 16:33:26 +0000133 list = append(list, fmt.Sprintf(`%s`+formatString(item)+`,`, innerIndent, item))
Liz Kammer72beb342022-02-03 08:42:10 -0500134 }
135 list = append(list, Indention(indentLevel)+"]")
136 return strings.Join(list, "\n")
137}
138
139// PrintStringListDict returns a Starlark-compatible string formatted as dictionary with
140// string keys and list of string values.
141func PrintStringListDict(dict map[string][]string, indentLevel int) string {
142 formattedValueDict := make(map[string]string, len(dict))
143 for k, v := range dict {
144 formattedValueDict[k] = PrintStringList(v, indentLevel+1)
145 }
146 return PrintDict(formattedValueDict, indentLevel)
147}
148
149// PrintBoolDict returns a starlark-compatible string containing a dictionary with string keys and
150// values printed with no additional formatting.
151func PrintBoolDict(dict map[string]bool, indentLevel int) string {
152 formattedValueDict := make(map[string]string, len(dict))
153 for k, v := range dict {
154 formattedValueDict[k] = PrintBool(v)
155 }
156 return PrintDict(formattedValueDict, indentLevel)
157}
158
Alix Espino4fd7e742023-02-24 14:46:43 +0000159// PrintStringIntDict returns a Starlark-compatible string formatted as dictionary with
160// string keys and int values.
161func PrintStringIntDict(dict map[string]int, indentLevel int) string {
162 valDict := make(map[string]string, len(dict))
163 for k, v := range dict {
164 valDict[k] = strconv.Itoa(v)
165 }
166 return PrintDict(valDict, indentLevel)
167}
168
Spandan Das6a448ec2023-04-19 17:36:12 +0000169// PrintStringStringDict returns a Starlark-compatible string formatted as dictionary with
170// string keys and string values.
171func PrintStringStringDict(dict map[string]string, indentLevel int) string {
172 valDict := make(map[string]string, len(dict))
173 for k, v := range dict {
174 valDict[k] = fmt.Sprintf(`"%s"`, v)
175 }
176 return PrintDict(valDict, indentLevel)
177}
178
Liz Kammer72beb342022-02-03 08:42:10 -0500179// PrintDict returns a starlark-compatible string containing a dictionary with string keys and
180// values printed with no additional formatting.
181func PrintDict(dict map[string]string, indentLevel int) string {
182 if len(dict) == 0 {
183 return "{}"
184 }
185 items := make([]string, 0, len(dict))
186 for k, v := range dict {
187 items = append(items, fmt.Sprintf(`%s"%s": %s,`, Indention(indentLevel+1), k, v))
188 }
189 sort.Strings(items)
190 return fmt.Sprintf(`{
191%s
192%s}`, strings.Join(items, "\n"), Indention(indentLevel))
193}