|
package tml |
|
|
|
import ( |
|
"bufio" |
|
"bytes" |
|
"errors" |
|
"io" |
|
"reflect" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
var ( |
|
errVariableLineFound = errors.New("variable line found") |
|
errCommentLineFound = errors.New("comment line found") |
|
) |
|
|
|
type Decoder struct { |
|
r *bufio.Reader |
|
} |
|
|
|
func (d *Decoder) readBytes(delim byte) (line []byte, err error) { |
|
line, err = d.r.ReadBytes(delim) |
|
return |
|
} |
|
|
|
// skipBytes 跳过所有内容直到读到 delim 字符 |
|
func (d *Decoder) skipBytes(delim byte) (err error) { |
|
var b byte |
|
for { |
|
if b, err = d.readByte(); err != nil { |
|
return |
|
} |
|
if b == delim { |
|
return nil |
|
} |
|
} |
|
} |
|
|
|
// skipWhitespace 跳过所有空白字符 |
|
func (d *Decoder) skipWhitespace() { |
|
var b byte |
|
var err error |
|
for { |
|
if b, err = d.peekByte(); err != nil { |
|
break |
|
} |
|
if b == ' ' || b == '\t' || b == '\n' || b == '\r' { |
|
_, _ = d.readByte() |
|
continue |
|
} |
|
break |
|
} |
|
} |
|
|
|
func (d *Decoder) readByte() (b byte, err error) { |
|
b, err = d.r.ReadByte() |
|
return |
|
} |
|
|
|
func (d *Decoder) peekByte() (b byte, err error) { |
|
ch, err := d.r.Peek(1) |
|
if err != nil { |
|
return |
|
} |
|
b = ch[0] |
|
return |
|
} |
|
|
|
func (d *Decoder) peekBytes(n int) (b []byte, err error) { |
|
b, err = d.r.Peek(n) |
|
return |
|
} |
|
|
|
func NewDecoder(r io.Reader) *Decoder { |
|
return &Decoder{r: bufio.NewReader(r)} |
|
} |
|
|
|
func (d *Decoder) Decode(val interface{}) error { |
|
rv := reflect.ValueOf(val) |
|
if rv.Kind() != reflect.Ptr || rv.IsNil() { |
|
return errors.New("unwritable type passed into decode") |
|
} |
|
return d.decodeInto(rv) |
|
} |
|
|
|
func DecodeString(in string, val interface{}) error { |
|
buf := strings.NewReader(in) |
|
d := NewDecoder(buf) |
|
return d.Decode(val) |
|
} |
|
|
|
func DecodeBytes(b []byte, val interface{}) error { |
|
r := bytes.NewReader(b) |
|
d := NewDecoder(r) |
|
return d.Decode(val) |
|
} |
|
|
|
// readVariable 一直读取到变量声明语句 |
|
// |
|
// 例如 @@ variable_name |
|
func (d *Decoder) readVariable() (string, error) { |
|
var err error |
|
var b byte |
|
var line []byte |
|
for { |
|
if err = d.skipBytes(variableDelimiter); err != nil && !errors.Is(err, io.EOF) { |
|
return "", err |
|
} |
|
if b, err = d.readByte(); err != nil && !errors.Is(err, io.EOF) { |
|
return "", err |
|
} |
|
if errors.Is(err, io.EOF) { |
|
return "", nil |
|
} |
|
if b != variableDelimiter { |
|
continue |
|
} |
|
if line, err = d.readBytes('\n'); err != nil && !errors.Is(err, io.EOF) { |
|
return "", err |
|
} |
|
return string(bytes.TrimSpace(line)), nil |
|
} |
|
} |
|
|
|
func (d *Decoder) readLine() ([]byte, error) { |
|
peeks, err := d.peekBytes(2) |
|
if err != nil && !errors.Is(err, io.EOF) { |
|
return nil, err |
|
} |
|
if len(peeks) == 2 && peeks[0] == variableDelimiter && peeks[1] == variableDelimiter { |
|
return nil, errVariableLineFound |
|
} |
|
line, err := d.readBytes('\n') |
|
if err != nil && !errors.Is(err, io.EOF) { |
|
return nil, err |
|
} |
|
if len(line) >= 2 && line[0] == commentDelimiter && line[1] == commentDelimiter { |
|
return nil, errCommentLineFound |
|
} |
|
return line, err |
|
} |
|
|
|
// readString 一直读取到下一个变量声明语句 |
|
func (d *Decoder) readString() (string, error) { |
|
var err error |
|
var b bytes.Buffer |
|
var line []byte |
|
for { |
|
line, err = d.readLine() |
|
if errors.Is(err, errVariableLineFound) { |
|
break |
|
} |
|
if errors.Is(err, errCommentLineFound) { |
|
continue |
|
} |
|
if _, e := b.Write(line); e != nil { |
|
return "", e |
|
} |
|
if errors.Is(err, io.EOF) { |
|
break |
|
} |
|
} |
|
return strings.TrimSpace(b.String()), nil |
|
} |
|
|
|
// readInt 读取整数 |
|
func (d *Decoder) readInt() (int, error) { |
|
var err error |
|
var line []byte |
|
var result int |
|
for { |
|
d.skipWhitespace() |
|
line, err = d.readLine() |
|
if errors.Is(err, errVariableLineFound) { |
|
break |
|
} |
|
if errors.Is(err, errCommentLineFound) { |
|
continue |
|
} |
|
line = bytes.TrimSpace(line) |
|
if result, err = strconv.Atoi(string(line)); err != nil { |
|
return 0, err |
|
} |
|
break |
|
} |
|
return result, nil |
|
} |
|
|
|
// readArray 读取数组 |
|
func (d *Decoder) readArray() ([]string, error) { |
|
var err error |
|
var line []byte |
|
result := make([]string, 0) |
|
for { |
|
d.skipWhitespace() |
|
line, err = d.readLine() |
|
if errors.Is(err, errVariableLineFound) { |
|
break |
|
} |
|
if errors.Is(err, errCommentLineFound) { |
|
continue |
|
} |
|
line = bytes.TrimSpace(line) |
|
if len(line) == 0 { |
|
break |
|
} |
|
arr := bytes.Split(line, []byte{arrayDelimiter}) |
|
result = make([]string, len(arr)) |
|
for i := range arr { |
|
result[i] = string(bytes.TrimSpace(arr[i])) |
|
} |
|
break |
|
} |
|
return result, nil |
|
} |
|
|
|
// readDict 读取字典 |
|
func (d *Decoder) readDict() (map[string]string, error) { |
|
var err error |
|
var peeks, line []byte |
|
result := make(map[string]string) |
|
for { |
|
d.skipWhitespace() |
|
if peeks, err = d.peekBytes(2); err != nil && !errors.Is(err, io.EOF) { |
|
return nil, err |
|
} |
|
if len(peeks) == 2 && peeks[0] == variableDelimiter && peeks[1] == variableDelimiter { |
|
break |
|
} |
|
if line, err = d.readBytes('\n'); err != nil && !errors.Is(err, io.EOF) { |
|
return nil, err |
|
} |
|
// 跳过注释行 |
|
if len(peeks) == 2 && peeks[0] == commentDelimiter && peeks[1] == commentDelimiter { |
|
continue |
|
} |
|
idx := bytes.IndexByte(line, dictDelimiter) |
|
if idx > 0 { |
|
strk := string(bytes.TrimSpace(line[:idx])) |
|
strv := string(bytes.TrimSpace(line[idx+1:])) |
|
result[strk] = strv |
|
} |
|
if errors.Is(err, io.EOF) { |
|
break |
|
} |
|
} |
|
return result, nil |
|
} |
|
|
|
func (d *Decoder) decodeField(field string, v reflect.Value) error { |
|
t := v.Elem().Type() |
|
var value reflect.Value |
|
for i := 0; i < v.Elem().NumField(); i++ { |
|
f := t.Field(i) |
|
if f.PkgPath != "" { |
|
continue |
|
} |
|
name, _ := parseTag(f.Tag.Get("tml")) |
|
if name == "" { |
|
name = f.Name |
|
} |
|
if !isValidTag(name) || name != field { |
|
continue |
|
} |
|
value = v.Elem().FieldByIndex(f.Index) |
|
} |
|
|
|
if value.Kind() == reflect.Invalid { |
|
return errors.New("field not found: " + field) |
|
} |
|
|
|
switch value.Kind() { |
|
case reflect.String: |
|
s, err := d.readString() |
|
if err != nil { |
|
return err |
|
} |
|
value.SetString(s) |
|
case reflect.Int: |
|
i, err := d.readInt() |
|
if err != nil { |
|
return err |
|
} |
|
value.SetInt(int64(i)) |
|
case reflect.Slice: |
|
if value.Type().Elem().Kind() != reflect.String { |
|
return errors.New("only support []string for slice type") |
|
} |
|
arr, err := d.readArray() |
|
if err != nil { |
|
return err |
|
} |
|
value.Set(reflect.ValueOf(arr)) |
|
case reflect.Map: |
|
if value.Type().Key().Kind() != reflect.String || value.Type().Elem().Kind() != reflect.String { |
|
return errors.New("only support map[string]string for map type") |
|
} |
|
dict, err := d.readDict() |
|
if err != nil { |
|
return err |
|
} |
|
value.Set(reflect.ValueOf(dict)) |
|
default: |
|
return errors.New("unsupported field type: " + value.Kind().String()) |
|
} |
|
return nil |
|
} |
|
|
|
func (d *Decoder) decodeInto(v reflect.Value) error { |
|
var ( |
|
fieldName string |
|
err error |
|
) |
|
for { |
|
if fieldName, err = d.readVariable(); err != nil { |
|
return err |
|
} |
|
if fieldName == "" { |
|
break |
|
} |
|
if err = d.decodeField(fieldName, v); err != nil { |
|
return err |
|
} |
|
if _, err = d.peekByte(); errors.Is(err, io.EOF) { |
|
break |
|
} |
|
} |
|
return nil |
|
} |