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  }