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 }