...

Source file src/github.com/xlab/treeprint/struct.go

Documentation: github.com/xlab/treeprint

     1  package treeprint
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  type StructTreeOption int
    10  
    11  const (
    12  	StructNameTree StructTreeOption = iota
    13  	StructValueTree
    14  	StructTagTree
    15  	StructTypeTree
    16  	StructTypeSizeTree
    17  )
    18  
    19  func FromStruct(v interface{}, opt ...StructTreeOption) (Tree, error) {
    20  	var treeOpt StructTreeOption
    21  	if len(opt) > 0 {
    22  		treeOpt = opt[0]
    23  	}
    24  	switch treeOpt {
    25  	case StructNameTree:
    26  		tree := New()
    27  		err := nameTree(tree, v)
    28  		return tree, err
    29  	case StructValueTree:
    30  		tree := New()
    31  		err := valueTree(tree, v)
    32  		return tree, err
    33  	case StructTagTree:
    34  		tree := New()
    35  		err := tagTree(tree, v)
    36  		return tree, err
    37  	case StructTypeTree:
    38  		tree := New()
    39  		err := typeTree(tree, v)
    40  		return tree, err
    41  	case StructTypeSizeTree:
    42  		tree := New()
    43  		err := typeSizeTree(tree, v)
    44  		return tree, err
    45  	default:
    46  		err := fmt.Errorf("treeprint: invalid StructTreeOption %v", treeOpt)
    47  		return nil, err
    48  	}
    49  }
    50  
    51  type FmtFunc func(name string, v interface{}) (string, bool)
    52  
    53  func FromStructWithMeta(v interface{}, fmtFunc FmtFunc) (Tree, error) {
    54  	if fmtFunc == nil {
    55  		tree := New()
    56  		err := nameTree(tree, v)
    57  		return tree, err
    58  	}
    59  	tree := New()
    60  	err := metaTree(tree, v, fmtFunc)
    61  	return tree, err
    62  }
    63  
    64  func Repr(v interface{}) string {
    65  	tree := New()
    66  	vType := reflect.TypeOf(v)
    67  	vValue := reflect.ValueOf(v)
    68  	_, val, isStruct := getValue(vType, &vValue)
    69  	if !isStruct {
    70  		return fmt.Sprintf("%+v", val.Interface())
    71  	}
    72  	err := valueTree(tree, val.Interface())
    73  	if err != nil {
    74  		return err.Error()
    75  	}
    76  	return tree.String()
    77  }
    78  
    79  func nameTree(tree Tree, v interface{}) error {
    80  	typ, val, err := checkType(v)
    81  	if err != nil {
    82  		return err
    83  	}
    84  	fields := typ.NumField()
    85  	for i := 0; i < fields; i++ {
    86  		field := typ.Field(i)
    87  		fieldValue := val.Field(i)
    88  		name, skip, omit := getMeta(field.Name, field.Tag)
    89  		if skip || omit && isEmpty(&fieldValue) {
    90  			continue
    91  		}
    92  		typ, val, isStruct := getValue(field.Type, &fieldValue)
    93  		if !isStruct {
    94  			tree.AddNode(name)
    95  			continue
    96  		} else if subNum := typ.NumField(); subNum == 0 {
    97  			tree.AddNode(name)
    98  			continue
    99  		}
   100  		branch := tree.AddBranch(name)
   101  		if err := nameTree(branch, val.Interface()); err != nil {
   102  			err := fmt.Errorf("%v on struct branch %s", err, name)
   103  			return err
   104  		}
   105  	}
   106  	return nil
   107  }
   108  
   109  func getMeta(fieldName string, tag reflect.StructTag) (name string, skip, omit bool) {
   110  	if tagStr := tag.Get("tree"); len(tagStr) > 0 {
   111  		name, omit = tagSpec(tagStr)
   112  	}
   113  	if name == "-" {
   114  		return fieldName, true, omit
   115  	}
   116  	if len(name) == 0 {
   117  		name = fieldName
   118  	} else if trimmed := strings.TrimSpace(name); len(trimmed) == 0 {
   119  		name = fieldName
   120  	}
   121  	return
   122  }
   123  
   124  func valueTree(tree Tree, v interface{}) error {
   125  	typ, val, err := checkType(v)
   126  	if err != nil {
   127  		return err
   128  	}
   129  	fields := typ.NumField()
   130  	for i := 0; i < fields; i++ {
   131  		field := typ.Field(i)
   132  		fieldValue := val.Field(i)
   133  		name, skip, omit := getMeta(field.Name, field.Tag)
   134  		if skip || omit && isEmpty(&fieldValue) {
   135  			continue
   136  		}
   137  		typ, val, isStruct := getValue(field.Type, &fieldValue)
   138  		if !isStruct {
   139  			tree.AddMetaNode(val.Interface(), name)
   140  			continue
   141  		} else if subNum := typ.NumField(); subNum == 0 {
   142  			tree.AddMetaNode(val.Interface(), name)
   143  			continue
   144  		}
   145  		branch := tree.AddBranch(name)
   146  		if err := valueTree(branch, val.Interface()); err != nil {
   147  			err := fmt.Errorf("%v on struct branch %s", err, name)
   148  			return err
   149  		}
   150  	}
   151  	return nil
   152  }
   153  
   154  func tagTree(tree Tree, v interface{}) error {
   155  	typ, val, err := checkType(v)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	fields := typ.NumField()
   160  	for i := 0; i < fields; i++ {
   161  		field := typ.Field(i)
   162  		fieldValue := val.Field(i)
   163  		name, skip, omit := getMeta(field.Name, field.Tag)
   164  		if skip || omit && isEmpty(&fieldValue) {
   165  			continue
   166  		}
   167  		filteredTag := filterTags(field.Tag)
   168  		typ, val, isStruct := getValue(field.Type, &fieldValue)
   169  		if !isStruct {
   170  			tree.AddMetaNode(filteredTag, name)
   171  			continue
   172  		} else if subNum := typ.NumField(); subNum == 0 {
   173  			tree.AddMetaNode(filteredTag, name)
   174  			continue
   175  		}
   176  		branch := tree.AddMetaBranch(filteredTag, name)
   177  		if err := tagTree(branch, val.Interface()); err != nil {
   178  			err := fmt.Errorf("%v on struct branch %s", err, name)
   179  			return err
   180  		}
   181  	}
   182  	return nil
   183  }
   184  
   185  func typeTree(tree Tree, v interface{}) error {
   186  	typ, val, err := checkType(v)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	fields := typ.NumField()
   191  	for i := 0; i < fields; i++ {
   192  		field := typ.Field(i)
   193  		fieldValue := val.Field(i)
   194  		name, skip, omit := getMeta(field.Name, field.Tag)
   195  		if skip || omit && isEmpty(&fieldValue) {
   196  			continue
   197  		}
   198  		typ, val, isStruct := getValue(field.Type, &fieldValue)
   199  		typename := fmt.Sprintf("%T", val.Interface())
   200  		if !isStruct {
   201  			tree.AddMetaNode(typename, name)
   202  			continue
   203  		} else if subNum := typ.NumField(); subNum == 0 {
   204  			tree.AddMetaNode(typename, name)
   205  			continue
   206  		}
   207  		branch := tree.AddMetaBranch(typename, name)
   208  		if err := typeTree(branch, val.Interface()); err != nil {
   209  			err := fmt.Errorf("%v on struct branch %s", err, name)
   210  			return err
   211  		}
   212  	}
   213  	return nil
   214  }
   215  
   216  func typeSizeTree(tree Tree, v interface{}) error {
   217  	typ, val, err := checkType(v)
   218  	if err != nil {
   219  		return err
   220  	}
   221  	fields := typ.NumField()
   222  	for i := 0; i < fields; i++ {
   223  		field := typ.Field(i)
   224  		fieldValue := val.Field(i)
   225  		name, skip, omit := getMeta(field.Name, field.Tag)
   226  		if skip || omit && isEmpty(&fieldValue) {
   227  			continue
   228  		}
   229  		typ, val, isStruct := getValue(field.Type, &fieldValue)
   230  		typesize := typ.Size()
   231  		if !isStruct {
   232  			tree.AddMetaNode(typesize, name)
   233  			continue
   234  		} else if subNum := typ.NumField(); subNum == 0 {
   235  			tree.AddMetaNode(typesize, name)
   236  			continue
   237  		}
   238  		branch := tree.AddMetaBranch(typesize, name)
   239  		if err := typeSizeTree(branch, val.Interface()); err != nil {
   240  			err := fmt.Errorf("%v on struct branch %s", err, name)
   241  			return err
   242  		}
   243  	}
   244  	return nil
   245  }
   246  
   247  func metaTree(tree Tree, v interface{}, fmtFunc FmtFunc) error {
   248  	typ, val, err := checkType(v)
   249  	if err != nil {
   250  		return err
   251  	}
   252  	fields := typ.NumField()
   253  	for i := 0; i < fields; i++ {
   254  		field := typ.Field(i)
   255  		fieldValue := val.Field(i)
   256  		name, skip, omit := getMeta(field.Name, field.Tag)
   257  		if skip || omit && isEmpty(&fieldValue) {
   258  			continue
   259  		}
   260  		typ, val, isStruct := getValue(field.Type, &fieldValue)
   261  		formatted, show := fmtFunc(name, val.Interface())
   262  		if !isStruct {
   263  			if show {
   264  				tree.AddMetaNode(formatted, name)
   265  				continue
   266  			}
   267  			tree.AddNode(name)
   268  			continue
   269  		} else if subNum := typ.NumField(); subNum == 0 {
   270  			if show {
   271  				tree.AddMetaNode(formatted, name)
   272  				continue
   273  			}
   274  			tree.AddNode(name)
   275  			continue
   276  		}
   277  		var branch Tree
   278  		if show {
   279  			branch = tree.AddMetaBranch(formatted, name)
   280  		} else {
   281  			branch = tree.AddBranch(name)
   282  		}
   283  		if err := metaTree(branch, val.Interface(), fmtFunc); err != nil {
   284  			err := fmt.Errorf("%v on struct branch %s", err, name)
   285  			return err
   286  		}
   287  	}
   288  	return nil
   289  }
   290  
   291  func getValue(typ reflect.Type, val *reflect.Value) (reflect.Type, *reflect.Value, bool) {
   292  	switch typ.Kind() {
   293  	case reflect.Ptr:
   294  		typ = typ.Elem()
   295  		if typ.Kind() == reflect.Struct {
   296  			elem := val.Elem()
   297  			return typ, &elem, true
   298  		}
   299  	case reflect.Struct:
   300  		return typ, val, true
   301  	}
   302  	return typ, val, false
   303  }
   304  
   305  func checkType(v interface{}) (reflect.Type, *reflect.Value, error) {
   306  	typ := reflect.TypeOf(v)
   307  	val := reflect.ValueOf(v)
   308  	switch typ.Kind() {
   309  	case reflect.Ptr:
   310  		typ = typ.Elem()
   311  		if typ.Kind() != reflect.Struct {
   312  			err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
   313  			return nil, nil, err
   314  		}
   315  		val = val.Elem()
   316  	case reflect.Struct:
   317  	default:
   318  		err := fmt.Errorf("treeprint: %T is not a struct we could work with", v)
   319  		return nil, nil, err
   320  	}
   321  	return typ, &val, nil
   322  }
   323  

View as plain text