github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/conf/section_config/section_config_decoder.go (about) 1 package section_config 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "go/ast" 8 "io" 9 "os" 10 "reflect" 11 12 "github.com/machinefi/w3bstream/pkg/depends/base/types" 13 "github.com/machinefi/w3bstream/pkg/depends/x/reflectx" 14 "github.com/machinefi/w3bstream/pkg/depends/x/textx" 15 ) 16 17 type Decoder struct { 18 Section 19 Sep byte 20 Values map[string]string 21 } 22 23 func NewDecoder(sep byte) *Decoder { 24 return &Decoder{ 25 Sep: sep, 26 Values: make(map[string]string), 27 } 28 } 29 30 func (d *Decoder) Unmarshal(c SectionConfig, data []byte) error { 31 rv := reflect.ValueOf(c) 32 33 if rv.Kind() == reflect.Ptr && rv.IsNil() { 34 panic("input is nil") 35 } 36 37 rv = reflectx.Indirect(rv) 38 39 if rv.Kind() != reflect.Struct { 40 panic("input should be a struct") 41 } 42 43 scanner := bufio.NewReader(bytes.NewBuffer(data)) 44 45 var ( 46 line []byte 47 err error 48 ) 49 for { 50 line, _, err = scanner.ReadLine() 51 if err != nil { 52 if err == io.EOF { 53 break 54 } 55 return err 56 } 57 line = bytes.TrimSpace(line) 58 if len(line) == 0 { 59 continue 60 } 61 if line[0] == '#' { 62 continue 63 } 64 if line[0] == '[' { 65 if line[len(line)-1] != ']' { 66 panic(fmt.Sprintf("unexpected section header: %s", string(line))) 67 } 68 line = line[1 : len(line)-1] 69 parts := bytes.SplitN(line, []byte{':'}, 2) 70 d.Name = string(parts[0]) 71 if len(parts) == 2 { 72 d.Value = string(parts[1]) 73 } 74 if d.Name != "" { 75 c.SetSection(d.Name, d.Value) 76 } 77 break 78 } else { 79 panic("should meet section header at beginning") 80 } 81 } 82 83 for { 84 line, _, err = scanner.ReadLine() 85 if err != nil { 86 if err == io.EOF { 87 break 88 } 89 return err 90 } 91 line = bytes.TrimSpace(line) 92 if len(line) == 0 { 93 continue 94 } 95 if line[0] == '#' { 96 continue 97 } 98 kv := bytes.SplitN(line, []byte{d.Sep}, 2) 99 kv[0] = bytes.TrimSpace(kv[0]) 100 d.Values[string(kv[0])] = "" 101 if len(kv) == 2 { 102 d.Values[string(kv[0])] = string(bytes.TrimSpace(kv[1])) 103 } 104 } 105 106 return d.scan(rv) 107 } 108 109 func (d *Decoder) UnmarshalFromFile(v SectionConfig, path string) error { 110 data, err := os.ReadFile(path) 111 if err != nil { 112 return err 113 } 114 return d.Unmarshal(v, data) 115 } 116 117 func (d *Decoder) scan(rv reflect.Value) error { 118 kind := rv.Kind() 119 120 if kind != reflect.Ptr && rv.CanAddr() { 121 if v, ok := rv.Addr().Interface().(types.DefaultSetter); ok { 122 v.SetDefault() 123 } 124 } 125 126 switch kind { 127 case reflect.Ptr: 128 if rv.IsNil() { 129 rv.Set(reflectx.New(rv.Type())) 130 } 131 return d.scan(rv.Elem()) 132 case reflect.Struct: 133 rt := rv.Type() 134 for i := 0; i < rt.NumField(); i++ { 135 ft, fv := rt.Field(i), rv.Field(i) 136 if !ast.IsExported(ft.Name) { 137 continue 138 } 139 if ft.Anonymous { 140 if err := d.scan(fv); err != nil { 141 return err 142 } 143 } 144 var ( 145 text []byte 146 err error 147 ) 148 tag, ok := ft.Tag.Lookup("name") 149 if !ok { 150 continue 151 } 152 key, _ := reflectx.TagValueAndFlags(tag) 153 if key == "-" { 154 continue 155 } 156 text = []byte(d.Values[key]) 157 if len(text) == 0 { 158 continue 159 } 160 if v, ok := fv.Addr().Interface().(types.TextUnmarshaler); ok { 161 err = v.UnmarshalText(text) 162 } else { 163 err = textx.UnmarshalText(fv, text) 164 } 165 if err != nil { 166 return err 167 } 168 } 169 default: 170 // skip 171 } 172 return nil 173 }