Merge pull request #323 from lizkammer/bpdocs
Add docs for nested and embedded structs
This commit is contained in:
commit
a10477e361
4 changed files with 158 additions and 22 deletions
|
@ -5,6 +5,7 @@ import (
|
|||
"html/template"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/google/blueprint/proptools"
|
||||
)
|
||||
|
@ -58,6 +59,7 @@ type Property struct {
|
|||
OtherTexts []template.HTML
|
||||
Properties []Property
|
||||
Default string
|
||||
Anonymous bool
|
||||
}
|
||||
|
||||
func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value,
|
||||
|
@ -75,6 +77,7 @@ func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[strin
|
|||
return nil, err
|
||||
}
|
||||
// Some pruning work
|
||||
removeAnonymousProperties(mtInfo)
|
||||
removeEmptyPropertyStructs(mtInfo)
|
||||
collapseDuplicatePropertyStructs(mtInfo)
|
||||
collapseNestedPropertyStructs(mtInfo)
|
||||
|
@ -128,7 +131,9 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
|
|||
}
|
||||
ps.ExcludeByTag("blueprint", "mutated")
|
||||
|
||||
for nestedName, nestedValue := range nestedPropertyStructs(v) {
|
||||
for _, nestedProperty := range nestedPropertyStructs(v) {
|
||||
nestedName := nestedProperty.nestPoint
|
||||
nestedValue := nestedProperty.value
|
||||
nestedType := nestedValue.Type()
|
||||
|
||||
// Ignore property structs with unexported or unnamed types
|
||||
|
@ -140,12 +145,27 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
|
|||
return nil, err
|
||||
}
|
||||
nested.ExcludeByTag("blueprint", "mutated")
|
||||
nestPoint := ps.GetByName(nestedName)
|
||||
if nestPoint == nil {
|
||||
return nil, fmt.Errorf("nesting point %q not found", nestedName)
|
||||
if nestedName == "" {
|
||||
ps.Nest(nested)
|
||||
} else {
|
||||
nestPoint := ps.GetByName(nestedName)
|
||||
if nestPoint == nil {
|
||||
return nil, fmt.Errorf("nesting point %q not found", nestedName)
|
||||
}
|
||||
nestPoint.Nest(nested)
|
||||
}
|
||||
|
||||
nestPoint.Nest(nested)
|
||||
if nestedProperty.anonymous {
|
||||
if nestedName != "" {
|
||||
nestedName += "."
|
||||
}
|
||||
nestedName += proptools.PropertyNameForField(nested.Name)
|
||||
nestedProp := ps.GetByName(nestedName)
|
||||
if nestedProp == nil {
|
||||
return nil, fmt.Errorf("could not find nested property %q", nestedName)
|
||||
}
|
||||
nestedProp.SetAnonymous()
|
||||
}
|
||||
}
|
||||
mt.PropertyStructs = append(mt.PropertyStructs, ps)
|
||||
}
|
||||
|
@ -153,10 +173,31 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
|
|||
return mt, nil
|
||||
}
|
||||
|
||||
func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
|
||||
ret := make(map[string]reflect.Value)
|
||||
type nestedProperty struct {
|
||||
nestPoint string
|
||||
value reflect.Value
|
||||
anonymous bool
|
||||
}
|
||||
|
||||
func nestedPropertyStructs(s reflect.Value) []nestedProperty {
|
||||
ret := make([]nestedProperty, 0)
|
||||
var walk func(structValue reflect.Value, prefix string)
|
||||
walk = func(structValue reflect.Value, prefix string) {
|
||||
var nestStruct func(field reflect.StructField, value reflect.Value, fieldName string)
|
||||
nestStruct = func(field reflect.StructField, value reflect.Value, fieldName string) {
|
||||
nestPoint := prefix
|
||||
if field.Anonymous {
|
||||
nestPoint = strings.TrimSuffix(nestPoint, ".")
|
||||
} else {
|
||||
nestPoint = nestPoint + proptools.PropertyNameForField(fieldName)
|
||||
}
|
||||
ret = append(ret, nestedProperty{nestPoint: nestPoint, value: value, anonymous: field.Anonymous})
|
||||
if nestPoint != "" {
|
||||
nestPoint += "."
|
||||
}
|
||||
walk(value, nestPoint)
|
||||
}
|
||||
|
||||
typ := structValue.Type()
|
||||
for i := 0; i < structValue.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
|
@ -174,8 +215,9 @@ func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
|
|||
case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
|
||||
// Nothing
|
||||
case reflect.Struct:
|
||||
walk(fieldValue, prefix+proptools.PropertyNameForField(field.Name)+".")
|
||||
nestStruct(field, fieldValue, field.Name)
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
|
||||
if !fieldValue.IsNil() {
|
||||
// We leave the pointer intact and zero out the struct that's
|
||||
// pointed to.
|
||||
|
@ -188,9 +230,7 @@ func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
|
|||
elem = elem.Elem()
|
||||
}
|
||||
if elem.Kind() == reflect.Struct {
|
||||
nestPoint := prefix + proptools.PropertyNameForField(field.Name)
|
||||
ret[nestPoint] = elem
|
||||
walk(elem, nestPoint+".")
|
||||
nestStruct(field, elem, field.Name)
|
||||
}
|
||||
}
|
||||
default:
|
||||
|
@ -214,6 +254,27 @@ func removeEmptyPropertyStructs(mt *ModuleType) {
|
|||
}
|
||||
}
|
||||
|
||||
// Remove any property structs that are anonymous
|
||||
func removeAnonymousProperties(mt *ModuleType) {
|
||||
var removeAnonymousProps func(props []Property) []Property
|
||||
removeAnonymousProps = func(props []Property) []Property {
|
||||
newProps := make([]Property, 0, len(props))
|
||||
for _, p := range props {
|
||||
if p.Anonymous {
|
||||
continue
|
||||
}
|
||||
if len(p.Properties) > 0 {
|
||||
p.Properties = removeAnonymousProps(p.Properties)
|
||||
}
|
||||
newProps = append(newProps, p)
|
||||
}
|
||||
return newProps
|
||||
}
|
||||
for _, ps := range mt.PropertyStructs {
|
||||
ps.Properties = removeAnonymousProps(ps.Properties)
|
||||
}
|
||||
}
|
||||
|
||||
// Squashes duplicates of the same property struct into single entries
|
||||
func collapseDuplicatePropertyStructs(mt *ModuleType) {
|
||||
var collapsed []*PropertyStruct
|
||||
|
|
|
@ -36,20 +36,23 @@ func TestNestedPropertyStructs(t *testing.T) {
|
|||
// mutated shouldn't be found because it's a mutated property.
|
||||
expected := []string{"child", "child.child"}
|
||||
if len(allStructs) != len(expected) {
|
||||
t.Errorf("expected %d structs, got %d, all entries: %q",
|
||||
t.Fatalf("expected %d structs, got %d, all entries: %v",
|
||||
len(expected), len(allStructs), allStructs)
|
||||
}
|
||||
for _, e := range expected {
|
||||
if _, ok := allStructs[e]; !ok {
|
||||
t.Errorf("missing entry %q, all entries: %q", e, allStructs)
|
||||
}
|
||||
got := []string{}
|
||||
for _, s := range allStructs {
|
||||
got = append(got, s.nestPoint)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("Expected nested properties:\n\t %q,\n but got\n\t %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllPackages(t *testing.T) {
|
||||
packages, err := AllPackages(pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs)
|
||||
if err != nil {
|
||||
t.Errorf("expected nil error for AllPackages(%v, %v, %v), got %s", pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs, err)
|
||||
t.Fatalf("expected nil error for AllPackages(%v, %v, %v), got %s", pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs, err)
|
||||
}
|
||||
|
||||
if numPackages := len(packages); numPackages != 1 {
|
||||
|
@ -58,24 +61,56 @@ func TestAllPackages(t *testing.T) {
|
|||
|
||||
pkg := packages[0]
|
||||
|
||||
expectedProps := map[string][]string{
|
||||
"bar": []string{
|
||||
"a",
|
||||
"nested",
|
||||
"nested.c",
|
||||
"nested_struct",
|
||||
"nested_struct.e",
|
||||
"struct_has_embed",
|
||||
"struct_has_embed.nested_in_embedded",
|
||||
"struct_has_embed.nested_in_embedded.e",
|
||||
"struct_has_embed.f",
|
||||
"nested_in_other_embedded",
|
||||
"nested_in_other_embedded.g",
|
||||
"h",
|
||||
},
|
||||
"foo": []string{
|
||||
"a",
|
||||
},
|
||||
}
|
||||
|
||||
for _, m := range pkg.ModuleTypes {
|
||||
foundProps := []string{}
|
||||
for _, p := range m.PropertyStructs {
|
||||
for _, err := range noMutatedProperties(p.Properties) {
|
||||
nestedProps, errs := findAllProperties("", p.Properties)
|
||||
foundProps = append(foundProps, nestedProps...)
|
||||
for _, err := range errs {
|
||||
t.Errorf("%s", err)
|
||||
}
|
||||
}
|
||||
if wanted, ok := expectedProps[m.Name]; ok {
|
||||
if !reflect.DeepEqual(foundProps, wanted) {
|
||||
t.Errorf("For %s, expected\n\t %q,\nbut got\n\t %q", m.Name, wanted, foundProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func noMutatedProperties(properties []Property) []error {
|
||||
func findAllProperties(prefix string, properties []Property) ([]string, []error) {
|
||||
foundProps := []string{}
|
||||
errs := []error{}
|
||||
for _, p := range properties {
|
||||
foundProps = append(foundProps, prefix+p.Name)
|
||||
if hasTag(p.Tag, "blueprint", "mutated") {
|
||||
err := fmt.Errorf("Property %s has `blueprint:\"mutated\" tag but should have been excluded.", p)
|
||||
err := fmt.Errorf("Property %s has `blueprint:\"mutated\" tag but should have been excluded.", p.Name)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
errs = append(errs, noMutatedProperties(p.Properties)...)
|
||||
nestedProps, nestedErrs := findAllProperties(prefix+p.Name+".", p.Properties)
|
||||
foundProps = append(foundProps, nestedProps...)
|
||||
errs = append(errs, nestedErrs...)
|
||||
}
|
||||
return errs
|
||||
return foundProps, errs
|
||||
}
|
||||
|
|
|
@ -142,6 +142,10 @@ func (ps *PropertyStruct) GetByName(name string) *Property {
|
|||
return getByName(name, "", &ps.Properties)
|
||||
}
|
||||
|
||||
func (ps *PropertyStruct) Nest(nested *PropertyStruct) {
|
||||
ps.Properties = append(ps.Properties, nested.Properties...)
|
||||
}
|
||||
|
||||
func getByName(name string, prefix string, props *[]Property) *Property {
|
||||
for i := range *props {
|
||||
if prefix+(*props)[i].Name == name {
|
||||
|
@ -157,6 +161,10 @@ func (p *Property) Nest(nested *PropertyStruct) {
|
|||
p.Properties = append(p.Properties, nested.Properties...)
|
||||
}
|
||||
|
||||
func (p *Property) SetAnonymous() {
|
||||
p.Anonymous = true
|
||||
}
|
||||
|
||||
func newPropertyStruct(t *doc.Type) (*PropertyStruct, error) {
|
||||
typeSpec := t.Decl.Specs[0].(*ast.TypeSpec)
|
||||
ps := PropertyStruct{
|
||||
|
|
|
@ -36,6 +36,32 @@ func barFactory() (blueprint.Module, []interface{}) {
|
|||
return nil, []interface{}{&complexProps{}}
|
||||
}
|
||||
|
||||
type structToNest struct {
|
||||
E string
|
||||
}
|
||||
|
||||
type StructToEmbed struct {
|
||||
Nested_in_embedded structToNest
|
||||
|
||||
// F string
|
||||
F string
|
||||
}
|
||||
|
||||
type otherStructToNest struct {
|
||||
G string
|
||||
}
|
||||
|
||||
type OtherStructToEmbed struct {
|
||||
Nested_in_other_embedded otherStructToNest
|
||||
|
||||
// F string
|
||||
H string
|
||||
}
|
||||
|
||||
type StructWithEmbedded struct {
|
||||
StructToEmbed
|
||||
}
|
||||
|
||||
// for bpdoc_test.go
|
||||
type complexProps struct {
|
||||
A string
|
||||
|
@ -45,6 +71,12 @@ type complexProps struct {
|
|||
C string
|
||||
D_mutated string `blueprint:"mutated"`
|
||||
}
|
||||
|
||||
Nested_struct structToNest
|
||||
|
||||
Struct_has_embed StructWithEmbedded
|
||||
|
||||
OtherStructToEmbed
|
||||
}
|
||||
|
||||
// props docs.
|
||||
|
|
Loading…
Reference in a new issue