go-hep.org/x/hep@v0.38.1/groot/rtree/wvar.go (about)

     1  // Copyright ©2020 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package rtree
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"regexp"
    11  	"strings"
    12  )
    13  
    14  // WriteVar describes a variable to be written out to a tree.
    15  type WriteVar struct {
    16  	Name  string // name of the variable
    17  	Value any    // pointer to the value to write
    18  	Count string // name of the branch holding the count-leaf value for slices
    19  }
    20  
    21  // WriteVarsFromStruct creates a slice of WriteVars from the ptr value.
    22  // WriteVarsFromStruct panics if ptr is not a pointer to a struct value.
    23  // WriteVarsFromStruct ignores fields that are not exported.
    24  func WriteVarsFromStruct(ptr any, opts ...WriteOption) []WriteVar {
    25  	cfg := wopt{
    26  		splitlvl: defaultSplitLevel,
    27  	}
    28  	for _, opt := range opts {
    29  		_ = opt(&cfg)
    30  	}
    31  
    32  	rv := reflect.ValueOf(ptr)
    33  	if rv.Kind() != reflect.Ptr {
    34  		panic(fmt.Errorf("rtree: expect a pointer value, got %T", ptr))
    35  	}
    36  
    37  	rv = rv.Elem()
    38  	if rv.Kind() != reflect.Struct {
    39  		panic(fmt.Errorf("rtree: expect a pointer to struct value, got %T", ptr))
    40  	}
    41  	var (
    42  		reDims = regexp.MustCompile(`\w*?\[(\w*)\]+?`)
    43  	)
    44  
    45  	split := func(s string) (string, []string) {
    46  		n := s
    47  		if i := strings.Index(s, "["); i > 0 {
    48  			n = s[:i]
    49  		}
    50  
    51  		out := reDims.FindAllStringSubmatch(s, -1)
    52  		if len(out) == 0 {
    53  			return n, nil
    54  		}
    55  
    56  		dims := make([]string, len(out))
    57  		for i := range out {
    58  			dims[i] = out[i][1]
    59  		}
    60  		return n, dims
    61  	}
    62  
    63  	if cfg.splitlvl == 0 {
    64  		name := cfg.title
    65  		if name == "" {
    66  			panic(fmt.Errorf("rtree: expect a title for split-less struct"))
    67  		}
    68  		return []WriteVar{
    69  			{Name: name, Value: ptr},
    70  		}
    71  	}
    72  
    73  	var (
    74  		rt    = rv.Type()
    75  		wvars = make([]WriteVar, 0, rt.NumField())
    76  	)
    77  
    78  	for i := range rt.NumField() {
    79  		var (
    80  			ft = rt.Field(i)
    81  			fv = rv.Field(i)
    82  		)
    83  		if ft.Name != toTitle(ft.Name) {
    84  			// not exported. ignore.
    85  			continue
    86  		}
    87  		wvar := WriteVar{
    88  			Name:  ft.Tag.Get("groot"),
    89  			Value: fv.Addr().Interface(),
    90  		}
    91  		if wvar.Name == "" {
    92  			wvar.Name = ft.Name
    93  		}
    94  
    95  		if strings.Contains(wvar.Name, "[") {
    96  			switch ft.Type.Kind() {
    97  			case reflect.Slice:
    98  				sli, dims := split(wvar.Name)
    99  				if len(dims) > 1 {
   100  					panic(fmt.Errorf("rtree: invalid number of slice-dimensions for field %q: %q", ft.Name, wvar.Name))
   101  				}
   102  				wvar.Name = sli
   103  				wvar.Count = dims[0]
   104  
   105  			case reflect.Array:
   106  				arr, dims := split(wvar.Name)
   107  				if len(dims) > 3 {
   108  					panic(fmt.Errorf("rtree: invalid number of array-dimension for field %q: %q", ft.Name, wvar.Name))
   109  				}
   110  				wvar.Name = arr
   111  			default:
   112  				panic(fmt.Errorf("rtree: invalid field type for %q, or invalid struct-tag %q: %T", ft.Name, wvar.Name, fv.Interface()))
   113  			}
   114  		}
   115  		switch ft.Type.Kind() {
   116  		case reflect.Int, reflect.Uint, reflect.UnsafePointer, reflect.Uintptr, reflect.Chan, reflect.Interface:
   117  			panic(fmt.Errorf("rtree: invalid field type for %q: %T", ft.Name, fv.Interface()))
   118  		case reflect.Map:
   119  			panic(fmt.Errorf("rtree: invalid field type for %q: %T (not yet supported)", ft.Name, fv.Interface()))
   120  		}
   121  
   122  		wvars = append(wvars, wvar)
   123  	}
   124  
   125  	return wvars
   126  }
   127  
   128  // WriteVarsFromTree creates a slice of WriteVars from the tree value.
   129  func WriteVarsFromTree(t Tree) []WriteVar {
   130  	rvars := NewReadVars(t)
   131  	wvars := make([]WriteVar, len(rvars))
   132  	for i, rvar := range rvars {
   133  		wvars[i] = WriteVar{
   134  			Name:  rvar.Name,
   135  			Value: reflect.New(reflect.TypeOf(rvar.Value).Elem()).Interface(),
   136  			Count: rvar.count,
   137  		}
   138  	}
   139  	return wvars
   140  }