xref: /aosp_15_r20/build/blueprint/bootstrap/bpdoc/properties.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2019 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage bpdoc
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
19*1fa6dee9SAndroid Build Coastguard Worker	"go/ast"
20*1fa6dee9SAndroid Build Coastguard Worker	"go/doc"
21*1fa6dee9SAndroid Build Coastguard Worker	"html/template"
22*1fa6dee9SAndroid Build Coastguard Worker	"reflect"
23*1fa6dee9SAndroid Build Coastguard Worker	"slices"
24*1fa6dee9SAndroid Build Coastguard Worker	"strconv"
25*1fa6dee9SAndroid Build Coastguard Worker	"strings"
26*1fa6dee9SAndroid Build Coastguard Worker	"unicode"
27*1fa6dee9SAndroid Build Coastguard Worker	"unicode/utf8"
28*1fa6dee9SAndroid Build Coastguard Worker
29*1fa6dee9SAndroid Build Coastguard Worker	"github.com/google/blueprint/proptools"
30*1fa6dee9SAndroid Build Coastguard Worker)
31*1fa6dee9SAndroid Build Coastguard Worker
32*1fa6dee9SAndroid Build Coastguard Worker//
33*1fa6dee9SAndroid Build Coastguard Worker// Utility functions for PropertyStruct and Property
34*1fa6dee9SAndroid Build Coastguard Worker//
35*1fa6dee9SAndroid Build Coastguard Worker
36*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) Clone() *PropertyStruct {
37*1fa6dee9SAndroid Build Coastguard Worker	ret := *ps
38*1fa6dee9SAndroid Build Coastguard Worker	ret.Properties = slices.Clone(ret.Properties)
39*1fa6dee9SAndroid Build Coastguard Worker	for i, prop := range ret.Properties {
40*1fa6dee9SAndroid Build Coastguard Worker		ret.Properties[i] = prop.Clone()
41*1fa6dee9SAndroid Build Coastguard Worker	}
42*1fa6dee9SAndroid Build Coastguard Worker
43*1fa6dee9SAndroid Build Coastguard Worker	return &ret
44*1fa6dee9SAndroid Build Coastguard Worker}
45*1fa6dee9SAndroid Build Coastguard Worker
46*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Clone() Property {
47*1fa6dee9SAndroid Build Coastguard Worker	ret := *p
48*1fa6dee9SAndroid Build Coastguard Worker	ret.Properties = slices.Clone(ret.Properties)
49*1fa6dee9SAndroid Build Coastguard Worker	for i, prop := range ret.Properties {
50*1fa6dee9SAndroid Build Coastguard Worker		ret.Properties[i] = prop.Clone()
51*1fa6dee9SAndroid Build Coastguard Worker	}
52*1fa6dee9SAndroid Build Coastguard Worker
53*1fa6dee9SAndroid Build Coastguard Worker	return ret
54*1fa6dee9SAndroid Build Coastguard Worker}
55*1fa6dee9SAndroid Build Coastguard Worker
56*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Equal(other Property) bool {
57*1fa6dee9SAndroid Build Coastguard Worker	return p.Name == other.Name && p.Type == other.Type && p.Tag == other.Tag &&
58*1fa6dee9SAndroid Build Coastguard Worker		p.Text == other.Text && p.Default == other.Default &&
59*1fa6dee9SAndroid Build Coastguard Worker		stringArrayEqual(p.OtherNames, other.OtherNames) &&
60*1fa6dee9SAndroid Build Coastguard Worker		htmlArrayEqual(p.OtherTexts, other.OtherTexts) &&
61*1fa6dee9SAndroid Build Coastguard Worker		p.SameSubProperties(other)
62*1fa6dee9SAndroid Build Coastguard Worker}
63*1fa6dee9SAndroid Build Coastguard Worker
64*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) SetDefaults(defaults reflect.Value) {
65*1fa6dee9SAndroid Build Coastguard Worker	setDefaults(ps.Properties, defaults)
66*1fa6dee9SAndroid Build Coastguard Worker}
67*1fa6dee9SAndroid Build Coastguard Worker
68*1fa6dee9SAndroid Build Coastguard Workerfunc setDefaults(properties []Property, defaults reflect.Value) {
69*1fa6dee9SAndroid Build Coastguard Worker	for i := range properties {
70*1fa6dee9SAndroid Build Coastguard Worker		prop := &properties[i]
71*1fa6dee9SAndroid Build Coastguard Worker		fieldName := proptools.FieldNameForProperty(prop.Name)
72*1fa6dee9SAndroid Build Coastguard Worker		f := defaults.FieldByName(fieldName)
73*1fa6dee9SAndroid Build Coastguard Worker		if (f == reflect.Value{}) {
74*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("property %q does not exist in %q", fieldName, defaults.Type()))
75*1fa6dee9SAndroid Build Coastguard Worker		}
76*1fa6dee9SAndroid Build Coastguard Worker
77*1fa6dee9SAndroid Build Coastguard Worker		if reflect.DeepEqual(f.Interface(), reflect.Zero(f.Type()).Interface()) {
78*1fa6dee9SAndroid Build Coastguard Worker			continue
79*1fa6dee9SAndroid Build Coastguard Worker		}
80*1fa6dee9SAndroid Build Coastguard Worker
81*1fa6dee9SAndroid Build Coastguard Worker		if f.Kind() == reflect.Interface {
82*1fa6dee9SAndroid Build Coastguard Worker			f = f.Elem()
83*1fa6dee9SAndroid Build Coastguard Worker		}
84*1fa6dee9SAndroid Build Coastguard Worker
85*1fa6dee9SAndroid Build Coastguard Worker		if f.Kind() == reflect.Ptr {
86*1fa6dee9SAndroid Build Coastguard Worker			if f.IsNil() {
87*1fa6dee9SAndroid Build Coastguard Worker				continue
88*1fa6dee9SAndroid Build Coastguard Worker			}
89*1fa6dee9SAndroid Build Coastguard Worker			f = f.Elem()
90*1fa6dee9SAndroid Build Coastguard Worker		}
91*1fa6dee9SAndroid Build Coastguard Worker
92*1fa6dee9SAndroid Build Coastguard Worker		if f.Kind() == reflect.Struct {
93*1fa6dee9SAndroid Build Coastguard Worker			setDefaults(prop.Properties, f)
94*1fa6dee9SAndroid Build Coastguard Worker		} else {
95*1fa6dee9SAndroid Build Coastguard Worker			prop.Default = fmt.Sprintf("%v", f.Interface())
96*1fa6dee9SAndroid Build Coastguard Worker		}
97*1fa6dee9SAndroid Build Coastguard Worker	}
98*1fa6dee9SAndroid Build Coastguard Worker}
99*1fa6dee9SAndroid Build Coastguard Worker
100*1fa6dee9SAndroid Build Coastguard Workerfunc stringArrayEqual(a, b []string) bool {
101*1fa6dee9SAndroid Build Coastguard Worker	if len(a) != len(b) {
102*1fa6dee9SAndroid Build Coastguard Worker		return false
103*1fa6dee9SAndroid Build Coastguard Worker	}
104*1fa6dee9SAndroid Build Coastguard Worker
105*1fa6dee9SAndroid Build Coastguard Worker	for i := range a {
106*1fa6dee9SAndroid Build Coastguard Worker		if a[i] != b[i] {
107*1fa6dee9SAndroid Build Coastguard Worker			return false
108*1fa6dee9SAndroid Build Coastguard Worker		}
109*1fa6dee9SAndroid Build Coastguard Worker	}
110*1fa6dee9SAndroid Build Coastguard Worker
111*1fa6dee9SAndroid Build Coastguard Worker	return true
112*1fa6dee9SAndroid Build Coastguard Worker}
113*1fa6dee9SAndroid Build Coastguard Worker
114*1fa6dee9SAndroid Build Coastguard Workerfunc htmlArrayEqual(a, b []template.HTML) bool {
115*1fa6dee9SAndroid Build Coastguard Worker	if len(a) != len(b) {
116*1fa6dee9SAndroid Build Coastguard Worker		return false
117*1fa6dee9SAndroid Build Coastguard Worker	}
118*1fa6dee9SAndroid Build Coastguard Worker
119*1fa6dee9SAndroid Build Coastguard Worker	for i := range a {
120*1fa6dee9SAndroid Build Coastguard Worker		if a[i] != b[i] {
121*1fa6dee9SAndroid Build Coastguard Worker			return false
122*1fa6dee9SAndroid Build Coastguard Worker		}
123*1fa6dee9SAndroid Build Coastguard Worker	}
124*1fa6dee9SAndroid Build Coastguard Worker
125*1fa6dee9SAndroid Build Coastguard Worker	return true
126*1fa6dee9SAndroid Build Coastguard Worker}
127*1fa6dee9SAndroid Build Coastguard Worker
128*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) SameSubProperties(other Property) bool {
129*1fa6dee9SAndroid Build Coastguard Worker	if len(p.Properties) != len(other.Properties) {
130*1fa6dee9SAndroid Build Coastguard Worker		return false
131*1fa6dee9SAndroid Build Coastguard Worker	}
132*1fa6dee9SAndroid Build Coastguard Worker
133*1fa6dee9SAndroid Build Coastguard Worker	for i := range p.Properties {
134*1fa6dee9SAndroid Build Coastguard Worker		if !p.Properties[i].Equal(other.Properties[i]) {
135*1fa6dee9SAndroid Build Coastguard Worker			return false
136*1fa6dee9SAndroid Build Coastguard Worker		}
137*1fa6dee9SAndroid Build Coastguard Worker	}
138*1fa6dee9SAndroid Build Coastguard Worker
139*1fa6dee9SAndroid Build Coastguard Worker	return true
140*1fa6dee9SAndroid Build Coastguard Worker}
141*1fa6dee9SAndroid Build Coastguard Worker
142*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) GetByName(name string) *Property {
143*1fa6dee9SAndroid Build Coastguard Worker	return getByName(name, "", &ps.Properties)
144*1fa6dee9SAndroid Build Coastguard Worker}
145*1fa6dee9SAndroid Build Coastguard Worker
146*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) Nest(nested *PropertyStruct) {
147*1fa6dee9SAndroid Build Coastguard Worker	ps.Properties = nestUnique(ps.Properties, nested.Properties)
148*1fa6dee9SAndroid Build Coastguard Worker}
149*1fa6dee9SAndroid Build Coastguard Worker
150*1fa6dee9SAndroid Build Coastguard Worker// Adds a target element to src if it does not exist in src
151*1fa6dee9SAndroid Build Coastguard Workerfunc nestUnique(src []Property, target []Property) []Property {
152*1fa6dee9SAndroid Build Coastguard Worker	var ret []Property
153*1fa6dee9SAndroid Build Coastguard Worker	ret = append(ret, src...)
154*1fa6dee9SAndroid Build Coastguard Worker	for _, elem := range target {
155*1fa6dee9SAndroid Build Coastguard Worker		isUnique := true
156*1fa6dee9SAndroid Build Coastguard Worker		for _, retElement := range ret {
157*1fa6dee9SAndroid Build Coastguard Worker			if elem.Equal(retElement) {
158*1fa6dee9SAndroid Build Coastguard Worker				isUnique = false
159*1fa6dee9SAndroid Build Coastguard Worker				break
160*1fa6dee9SAndroid Build Coastguard Worker			}
161*1fa6dee9SAndroid Build Coastguard Worker		}
162*1fa6dee9SAndroid Build Coastguard Worker		if isUnique {
163*1fa6dee9SAndroid Build Coastguard Worker			ret = append(ret, elem)
164*1fa6dee9SAndroid Build Coastguard Worker		}
165*1fa6dee9SAndroid Build Coastguard Worker	}
166*1fa6dee9SAndroid Build Coastguard Worker	return ret
167*1fa6dee9SAndroid Build Coastguard Worker}
168*1fa6dee9SAndroid Build Coastguard Worker
169*1fa6dee9SAndroid Build Coastguard Workerfunc getByName(name string, prefix string, props *[]Property) *Property {
170*1fa6dee9SAndroid Build Coastguard Worker	for i := range *props {
171*1fa6dee9SAndroid Build Coastguard Worker		if prefix+(*props)[i].Name == name {
172*1fa6dee9SAndroid Build Coastguard Worker			return &(*props)[i]
173*1fa6dee9SAndroid Build Coastguard Worker		} else if strings.HasPrefix(name, prefix+(*props)[i].Name+".") {
174*1fa6dee9SAndroid Build Coastguard Worker			return getByName(name, prefix+(*props)[i].Name+".", &(*props)[i].Properties)
175*1fa6dee9SAndroid Build Coastguard Worker		}
176*1fa6dee9SAndroid Build Coastguard Worker	}
177*1fa6dee9SAndroid Build Coastguard Worker	return nil
178*1fa6dee9SAndroid Build Coastguard Worker}
179*1fa6dee9SAndroid Build Coastguard Worker
180*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) Nest(nested *PropertyStruct) {
181*1fa6dee9SAndroid Build Coastguard Worker	p.Properties = nestUnique(p.Properties, nested.Properties)
182*1fa6dee9SAndroid Build Coastguard Worker}
183*1fa6dee9SAndroid Build Coastguard Worker
184*1fa6dee9SAndroid Build Coastguard Workerfunc (p *Property) SetAnonymous() {
185*1fa6dee9SAndroid Build Coastguard Worker	p.Anonymous = true
186*1fa6dee9SAndroid Build Coastguard Worker}
187*1fa6dee9SAndroid Build Coastguard Worker
188*1fa6dee9SAndroid Build Coastguard Workerfunc newPropertyStruct(t *doc.Type) (*PropertyStruct, error) {
189*1fa6dee9SAndroid Build Coastguard Worker	typeSpec := t.Decl.Specs[0].(*ast.TypeSpec)
190*1fa6dee9SAndroid Build Coastguard Worker	ps := PropertyStruct{
191*1fa6dee9SAndroid Build Coastguard Worker		Name: t.Name,
192*1fa6dee9SAndroid Build Coastguard Worker		Text: t.Doc,
193*1fa6dee9SAndroid Build Coastguard Worker	}
194*1fa6dee9SAndroid Build Coastguard Worker
195*1fa6dee9SAndroid Build Coastguard Worker	structType, ok := typeSpec.Type.(*ast.StructType)
196*1fa6dee9SAndroid Build Coastguard Worker	if !ok {
197*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("type of %q is not a struct", t.Name)
198*1fa6dee9SAndroid Build Coastguard Worker	}
199*1fa6dee9SAndroid Build Coastguard Worker
200*1fa6dee9SAndroid Build Coastguard Worker	var err error
201*1fa6dee9SAndroid Build Coastguard Worker	ps.Properties, err = structProperties(structType)
202*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
203*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
204*1fa6dee9SAndroid Build Coastguard Worker	}
205*1fa6dee9SAndroid Build Coastguard Worker
206*1fa6dee9SAndroid Build Coastguard Worker	return &ps, nil
207*1fa6dee9SAndroid Build Coastguard Worker}
208*1fa6dee9SAndroid Build Coastguard Worker
209*1fa6dee9SAndroid Build Coastguard Workerfunc structProperties(structType *ast.StructType) (props []Property, err error) {
210*1fa6dee9SAndroid Build Coastguard Worker	for _, f := range structType.Fields.List {
211*1fa6dee9SAndroid Build Coastguard Worker		names := f.Names
212*1fa6dee9SAndroid Build Coastguard Worker		if names == nil {
213*1fa6dee9SAndroid Build Coastguard Worker			// Anonymous fields have no name, use the type as the name
214*1fa6dee9SAndroid Build Coastguard Worker			// TODO: hide the name and make the properties show up in the embedding struct
215*1fa6dee9SAndroid Build Coastguard Worker			if t, ok := f.Type.(*ast.Ident); ok {
216*1fa6dee9SAndroid Build Coastguard Worker				names = append(names, t)
217*1fa6dee9SAndroid Build Coastguard Worker			}
218*1fa6dee9SAndroid Build Coastguard Worker		}
219*1fa6dee9SAndroid Build Coastguard Worker		for _, n := range names {
220*1fa6dee9SAndroid Build Coastguard Worker			var name, tag, text string
221*1fa6dee9SAndroid Build Coastguard Worker			if n != nil {
222*1fa6dee9SAndroid Build Coastguard Worker				name = proptools.PropertyNameForField(n.Name)
223*1fa6dee9SAndroid Build Coastguard Worker			}
224*1fa6dee9SAndroid Build Coastguard Worker			if f.Doc != nil {
225*1fa6dee9SAndroid Build Coastguard Worker				text = f.Doc.Text()
226*1fa6dee9SAndroid Build Coastguard Worker			}
227*1fa6dee9SAndroid Build Coastguard Worker			if f.Tag != nil {
228*1fa6dee9SAndroid Build Coastguard Worker				tag, err = strconv.Unquote(f.Tag.Value)
229*1fa6dee9SAndroid Build Coastguard Worker				if err != nil {
230*1fa6dee9SAndroid Build Coastguard Worker					return nil, err
231*1fa6dee9SAndroid Build Coastguard Worker				}
232*1fa6dee9SAndroid Build Coastguard Worker			}
233*1fa6dee9SAndroid Build Coastguard Worker			typ, innerProps, err := getType(f.Type)
234*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
235*1fa6dee9SAndroid Build Coastguard Worker				return nil, err
236*1fa6dee9SAndroid Build Coastguard Worker			}
237*1fa6dee9SAndroid Build Coastguard Worker
238*1fa6dee9SAndroid Build Coastguard Worker			props = append(props, Property{
239*1fa6dee9SAndroid Build Coastguard Worker				Name:       name,
240*1fa6dee9SAndroid Build Coastguard Worker				Type:       typ,
241*1fa6dee9SAndroid Build Coastguard Worker				Tag:        reflect.StructTag(tag),
242*1fa6dee9SAndroid Build Coastguard Worker				Text:       formatText(text),
243*1fa6dee9SAndroid Build Coastguard Worker				Properties: innerProps,
244*1fa6dee9SAndroid Build Coastguard Worker			})
245*1fa6dee9SAndroid Build Coastguard Worker		}
246*1fa6dee9SAndroid Build Coastguard Worker	}
247*1fa6dee9SAndroid Build Coastguard Worker
248*1fa6dee9SAndroid Build Coastguard Worker	return props, nil
249*1fa6dee9SAndroid Build Coastguard Worker}
250*1fa6dee9SAndroid Build Coastguard Worker
251*1fa6dee9SAndroid Build Coastguard Workerfunc getType(expr ast.Expr) (typ string, innerProps []Property, err error) {
252*1fa6dee9SAndroid Build Coastguard Worker	var t ast.Expr
253*1fa6dee9SAndroid Build Coastguard Worker	if star, ok := expr.(*ast.StarExpr); ok {
254*1fa6dee9SAndroid Build Coastguard Worker		t = star.X
255*1fa6dee9SAndroid Build Coastguard Worker	} else {
256*1fa6dee9SAndroid Build Coastguard Worker		t = expr
257*1fa6dee9SAndroid Build Coastguard Worker	}
258*1fa6dee9SAndroid Build Coastguard Worker	switch a := t.(type) {
259*1fa6dee9SAndroid Build Coastguard Worker	case *ast.ArrayType:
260*1fa6dee9SAndroid Build Coastguard Worker		var elt string
261*1fa6dee9SAndroid Build Coastguard Worker		elt, innerProps, err = getType(a.Elt)
262*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
263*1fa6dee9SAndroid Build Coastguard Worker			return "", nil, err
264*1fa6dee9SAndroid Build Coastguard Worker		}
265*1fa6dee9SAndroid Build Coastguard Worker		typ = "list of " + elt
266*1fa6dee9SAndroid Build Coastguard Worker	case *ast.InterfaceType:
267*1fa6dee9SAndroid Build Coastguard Worker		typ = "interface"
268*1fa6dee9SAndroid Build Coastguard Worker	case *ast.Ident:
269*1fa6dee9SAndroid Build Coastguard Worker		typ = a.Name
270*1fa6dee9SAndroid Build Coastguard Worker	case *ast.StructType:
271*1fa6dee9SAndroid Build Coastguard Worker		innerProps, err = structProperties(a)
272*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
273*1fa6dee9SAndroid Build Coastguard Worker			return "", nil, err
274*1fa6dee9SAndroid Build Coastguard Worker		}
275*1fa6dee9SAndroid Build Coastguard Worker	case *ast.IndexExpr:
276*1fa6dee9SAndroid Build Coastguard Worker		// IndexExpr is used to represent generic type arguments
277*1fa6dee9SAndroid Build Coastguard Worker		if !isConfigurableAst(a.X) {
278*1fa6dee9SAndroid Build Coastguard Worker			var writer strings.Builder
279*1fa6dee9SAndroid Build Coastguard Worker			if err := ast.Fprint(&writer, nil, expr, nil); err != nil {
280*1fa6dee9SAndroid Build Coastguard Worker				return "", nil, err
281*1fa6dee9SAndroid Build Coastguard Worker			}
282*1fa6dee9SAndroid Build Coastguard Worker			return "", nil, fmt.Errorf("unknown type %s", writer.String())
283*1fa6dee9SAndroid Build Coastguard Worker		}
284*1fa6dee9SAndroid Build Coastguard Worker		var innerType string
285*1fa6dee9SAndroid Build Coastguard Worker		innerType, innerProps, err = getType(a.Index)
286*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
287*1fa6dee9SAndroid Build Coastguard Worker			return "", nil, err
288*1fa6dee9SAndroid Build Coastguard Worker		}
289*1fa6dee9SAndroid Build Coastguard Worker		typ = "configurable " + innerType
290*1fa6dee9SAndroid Build Coastguard Worker	default:
291*1fa6dee9SAndroid Build Coastguard Worker		typ = fmt.Sprintf("%T", expr)
292*1fa6dee9SAndroid Build Coastguard Worker	}
293*1fa6dee9SAndroid Build Coastguard Worker
294*1fa6dee9SAndroid Build Coastguard Worker	return typ, innerProps, nil
295*1fa6dee9SAndroid Build Coastguard Worker}
296*1fa6dee9SAndroid Build Coastguard Worker
297*1fa6dee9SAndroid Build Coastguard Workerfunc isConfigurableAst(expr ast.Expr) bool {
298*1fa6dee9SAndroid Build Coastguard Worker	switch e := expr.(type) {
299*1fa6dee9SAndroid Build Coastguard Worker	case *ast.Ident:
300*1fa6dee9SAndroid Build Coastguard Worker		return e.Name == "Configurable"
301*1fa6dee9SAndroid Build Coastguard Worker	case *ast.SelectorExpr:
302*1fa6dee9SAndroid Build Coastguard Worker		if l, ok := e.X.(*ast.Ident); ok && l.Name == "proptools" {
303*1fa6dee9SAndroid Build Coastguard Worker			if e.Sel.Name == "Configurable" {
304*1fa6dee9SAndroid Build Coastguard Worker				return true
305*1fa6dee9SAndroid Build Coastguard Worker			}
306*1fa6dee9SAndroid Build Coastguard Worker		}
307*1fa6dee9SAndroid Build Coastguard Worker	}
308*1fa6dee9SAndroid Build Coastguard Worker	return false
309*1fa6dee9SAndroid Build Coastguard Worker}
310*1fa6dee9SAndroid Build Coastguard Worker
311*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) ExcludeByTag(key, value string) {
312*1fa6dee9SAndroid Build Coastguard Worker	filterPropsByTag(&ps.Properties, key, value, true)
313*1fa6dee9SAndroid Build Coastguard Worker}
314*1fa6dee9SAndroid Build Coastguard Worker
315*1fa6dee9SAndroid Build Coastguard Workerfunc (ps *PropertyStruct) IncludeByTag(key, value string) {
316*1fa6dee9SAndroid Build Coastguard Worker	filterPropsByTag(&ps.Properties, key, value, false)
317*1fa6dee9SAndroid Build Coastguard Worker}
318*1fa6dee9SAndroid Build Coastguard Worker
319*1fa6dee9SAndroid Build Coastguard Workerfunc filterPropsByTag(props *[]Property, key, value string, exclude bool) {
320*1fa6dee9SAndroid Build Coastguard Worker	// Create a slice that shares the storage of props but has 0 length.  Appending up to
321*1fa6dee9SAndroid Build Coastguard Worker	// len(props) times to this slice will overwrite the original slice contents
322*1fa6dee9SAndroid Build Coastguard Worker	filtered := (*props)[:0]
323*1fa6dee9SAndroid Build Coastguard Worker	for _, x := range *props {
324*1fa6dee9SAndroid Build Coastguard Worker		if hasTag(x.Tag, key, value) == !exclude {
325*1fa6dee9SAndroid Build Coastguard Worker			filterPropsByTag(&x.Properties, key, value, exclude)
326*1fa6dee9SAndroid Build Coastguard Worker			filtered = append(filtered, x)
327*1fa6dee9SAndroid Build Coastguard Worker		}
328*1fa6dee9SAndroid Build Coastguard Worker	}
329*1fa6dee9SAndroid Build Coastguard Worker
330*1fa6dee9SAndroid Build Coastguard Worker	*props = filtered
331*1fa6dee9SAndroid Build Coastguard Worker}
332*1fa6dee9SAndroid Build Coastguard Worker
333*1fa6dee9SAndroid Build Coastguard Workerfunc hasTag(tag reflect.StructTag, key, value string) bool {
334*1fa6dee9SAndroid Build Coastguard Worker	for _, entry := range strings.Split(tag.Get(key), ",") {
335*1fa6dee9SAndroid Build Coastguard Worker		if entry == value {
336*1fa6dee9SAndroid Build Coastguard Worker			return true
337*1fa6dee9SAndroid Build Coastguard Worker		}
338*1fa6dee9SAndroid Build Coastguard Worker	}
339*1fa6dee9SAndroid Build Coastguard Worker	return false
340*1fa6dee9SAndroid Build Coastguard Worker}
341*1fa6dee9SAndroid Build Coastguard Worker
342*1fa6dee9SAndroid Build Coastguard Workerfunc formatText(text string) template.HTML {
343*1fa6dee9SAndroid Build Coastguard Worker	var html template.HTML
344*1fa6dee9SAndroid Build Coastguard Worker	lines := strings.Split(text, "\n")
345*1fa6dee9SAndroid Build Coastguard Worker	preformatted := false
346*1fa6dee9SAndroid Build Coastguard Worker	for _, line := range lines {
347*1fa6dee9SAndroid Build Coastguard Worker		r, _ := utf8.DecodeRuneInString(line)
348*1fa6dee9SAndroid Build Coastguard Worker		indent := unicode.IsSpace(r)
349*1fa6dee9SAndroid Build Coastguard Worker		if indent && !preformatted {
350*1fa6dee9SAndroid Build Coastguard Worker			html += "<pre>\n\n"
351*1fa6dee9SAndroid Build Coastguard Worker			preformatted = true
352*1fa6dee9SAndroid Build Coastguard Worker		} else if !indent && line != "" && preformatted {
353*1fa6dee9SAndroid Build Coastguard Worker			html += "</pre>\n"
354*1fa6dee9SAndroid Build Coastguard Worker			preformatted = false
355*1fa6dee9SAndroid Build Coastguard Worker		}
356*1fa6dee9SAndroid Build Coastguard Worker		html += template.HTML(template.HTMLEscapeString(line)) + "\n"
357*1fa6dee9SAndroid Build Coastguard Worker	}
358*1fa6dee9SAndroid Build Coastguard Worker	if preformatted {
359*1fa6dee9SAndroid Build Coastguard Worker		html += "</pre>\n"
360*1fa6dee9SAndroid Build Coastguard Worker	}
361*1fa6dee9SAndroid Build Coastguard Worker	return html
362*1fa6dee9SAndroid Build Coastguard Worker}
363