mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-03 19:17:29 -05:00 
			
		
		
		
	The vendor/ folder was created with the help of @FiloSottile's gvt and vendorcheck. Any dependencies of Caddy plugins outside this repo are not vendored. We do not remove any unused, vendored packages because vendorcheck -u only checks using the current build configuration; i.e. packages that may be imported by files toggled by build tags of other systems. CI tests have been updated to ignore the vendor/ folder. When Go 1.9 is released, a few of the go commands should be revised to again use ./... as it will ignore the vendor folder by default.
		
			
				
	
	
		
			399 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package toml
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/naoina/toml/ast"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	tagOmitempty = "omitempty"
 | 
						|
	tagSkip      = "-"
 | 
						|
)
 | 
						|
 | 
						|
// Marshal returns the TOML encoding of v.
 | 
						|
//
 | 
						|
// Struct values encode as TOML. Each exported struct field becomes a field of
 | 
						|
// the TOML structure unless
 | 
						|
//   - the field's tag is "-", or
 | 
						|
//   - the field is empty and its tag specifies the "omitempty" option.
 | 
						|
//
 | 
						|
// The "toml" key in the struct field's tag value is the key name, followed by
 | 
						|
// an optional comma and options. Examples:
 | 
						|
//
 | 
						|
//   // Field is ignored by this package.
 | 
						|
//   Field int `toml:"-"`
 | 
						|
//
 | 
						|
//   // Field appears in TOML as key "myName".
 | 
						|
//   Field int `toml:"myName"`
 | 
						|
//
 | 
						|
//   // Field appears in TOML as key "myName" and the field is omitted from the
 | 
						|
//   // result of encoding if its value is empty.
 | 
						|
//   Field int `toml:"myName,omitempty"`
 | 
						|
//
 | 
						|
//   // Field appears in TOML as key "field", but the field is skipped if
 | 
						|
//   // empty. Note the leading comma.
 | 
						|
//   Field int `toml:",omitempty"`
 | 
						|
func (cfg *Config) Marshal(v interface{}) ([]byte, error) {
 | 
						|
	buf := new(bytes.Buffer)
 | 
						|
	err := cfg.NewEncoder(buf).Encode(v)
 | 
						|
	return buf.Bytes(), err
 | 
						|
}
 | 
						|
 | 
						|
// A Encoder writes TOML to an output stream.
 | 
						|
type Encoder struct {
 | 
						|
	w   io.Writer
 | 
						|
	cfg *Config
 | 
						|
}
 | 
						|
 | 
						|
// NewEncoder returns a new Encoder that writes to w.
 | 
						|
func (cfg *Config) NewEncoder(w io.Writer) *Encoder {
 | 
						|
	return &Encoder{w, cfg}
 | 
						|
}
 | 
						|
 | 
						|
// Encode writes the TOML of v to the stream.
 | 
						|
// See the documentation for Marshal for details about the conversion of Go values to TOML.
 | 
						|
func (e *Encoder) Encode(v interface{}) error {
 | 
						|
	rv := reflect.ValueOf(v)
 | 
						|
	for rv.Kind() == reflect.Ptr {
 | 
						|
		if rv.IsNil() {
 | 
						|
			return &marshalNilError{rv.Type()}
 | 
						|
		}
 | 
						|
		rv = rv.Elem()
 | 
						|
	}
 | 
						|
	buf := &tableBuf{typ: ast.TableTypeNormal}
 | 
						|
	var err error
 | 
						|
	switch rv.Kind() {
 | 
						|
	case reflect.Struct:
 | 
						|
		err = buf.structFields(e.cfg, rv)
 | 
						|
	case reflect.Map:
 | 
						|
		err = buf.mapFields(e.cfg, rv)
 | 
						|
	default:
 | 
						|
		err = &marshalTableError{rv.Type()}
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return buf.writeTo(e.w, "")
 | 
						|
}
 | 
						|
 | 
						|
// Marshaler can be implemented to override the encoding of TOML values. The returned text
 | 
						|
// must be a simple TOML value (i.e. not a table) and is inserted into marshaler output.
 | 
						|
//
 | 
						|
// This interface exists for backwards-compatibility reasons. You probably want to
 | 
						|
// implement encoding.TextMarshaler or MarshalerRec instead.
 | 
						|
type Marshaler interface {
 | 
						|
	MarshalTOML() ([]byte, error)
 | 
						|
}
 | 
						|
 | 
						|
// MarshalerRec can be implemented to override the TOML encoding of a type.
 | 
						|
// The returned value is marshaled in place of the receiver.
 | 
						|
type MarshalerRec interface {
 | 
						|
	MarshalTOML() (interface{}, error)
 | 
						|
}
 | 
						|
 | 
						|
type tableBuf struct {
 | 
						|
	name       string // already escaped / quoted
 | 
						|
	body       []byte
 | 
						|
	children   []*tableBuf
 | 
						|
	typ        ast.TableType
 | 
						|
	arrayDepth int
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) writeTo(w io.Writer, prefix string) error {
 | 
						|
	key := b.name // TODO: escape dots
 | 
						|
	if prefix != "" {
 | 
						|
		key = prefix + "." + key
 | 
						|
	}
 | 
						|
 | 
						|
	if b.name != "" {
 | 
						|
		head := "[" + key + "]"
 | 
						|
		if b.typ == ast.TableTypeArray {
 | 
						|
			head = "[" + head + "]"
 | 
						|
		}
 | 
						|
		head += "\n"
 | 
						|
		if _, err := io.WriteString(w, head); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if _, err := w.Write(b.body); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	for i, child := range b.children {
 | 
						|
		if len(b.body) > 0 || i > 0 {
 | 
						|
			if _, err := w.Write([]byte("\n")); err != nil {
 | 
						|
				return err
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if err := child.writeTo(w, key); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) newChild(name string) *tableBuf {
 | 
						|
	child := &tableBuf{name: quoteName(name), typ: ast.TableTypeNormal}
 | 
						|
	if b.arrayDepth > 0 {
 | 
						|
		child.typ = ast.TableTypeArray
 | 
						|
	}
 | 
						|
	return child
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) addChild(child *tableBuf) {
 | 
						|
	// Empty table elision: we can avoid writing a table that doesn't have any keys on its
 | 
						|
	// own. Array tables can't be elided because they define array elements (which would
 | 
						|
	// be missing if elided).
 | 
						|
	if len(child.body) == 0 && child.typ == ast.TableTypeNormal {
 | 
						|
		for _, gchild := range child.children {
 | 
						|
			gchild.name = child.name + "." + gchild.name
 | 
						|
			b.addChild(gchild)
 | 
						|
		}
 | 
						|
		return
 | 
						|
	}
 | 
						|
	b.children = append(b.children, child)
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) structFields(cfg *Config, rv reflect.Value) error {
 | 
						|
	rt := rv.Type()
 | 
						|
	for i := 0; i < rv.NumField(); i++ {
 | 
						|
		ft := rt.Field(i)
 | 
						|
		if ft.PkgPath != "" && !ft.Anonymous { // not exported
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		name, rest := extractTag(ft.Tag.Get(fieldTagName))
 | 
						|
		if name == tagSkip {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		fv := rv.Field(i)
 | 
						|
		if rest == tagOmitempty && isEmptyValue(fv) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		if name == "" {
 | 
						|
			name = cfg.FieldToKey(rt, ft.Name)
 | 
						|
		}
 | 
						|
		if err := b.field(cfg, name, fv); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
type mapKeyList []struct {
 | 
						|
	key   string
 | 
						|
	value reflect.Value
 | 
						|
}
 | 
						|
 | 
						|
func (l mapKeyList) Len() int           { return len(l) }
 | 
						|
func (l mapKeyList) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
 | 
						|
func (l mapKeyList) Less(i, j int) bool { return l[i].key < l[j].key }
 | 
						|
 | 
						|
func (b *tableBuf) mapFields(cfg *Config, rv reflect.Value) error {
 | 
						|
	keys := rv.MapKeys()
 | 
						|
	keylist := make(mapKeyList, len(keys))
 | 
						|
	for i, key := range keys {
 | 
						|
		var err error
 | 
						|
		keylist[i].key, err = encodeMapKey(key)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		keylist[i].value = rv.MapIndex(key)
 | 
						|
	}
 | 
						|
	sort.Sort(keylist)
 | 
						|
 | 
						|
	for _, kv := range keylist {
 | 
						|
		if err := b.field(cfg, kv.key, kv.value); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) field(cfg *Config, name string, rv reflect.Value) error {
 | 
						|
	off := len(b.body)
 | 
						|
	b.body = append(b.body, quoteName(name)...)
 | 
						|
	b.body = append(b.body, " = "...)
 | 
						|
	isTable, err := b.value(cfg, rv, name)
 | 
						|
	if isTable {
 | 
						|
		b.body = b.body[:off] // rub out "key ="
 | 
						|
	} else {
 | 
						|
		b.body = append(b.body, '\n')
 | 
						|
	}
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) value(cfg *Config, rv reflect.Value, name string) (bool, error) {
 | 
						|
	isMarshaler, isTable, err := b.marshaler(cfg, rv, name)
 | 
						|
	if isMarshaler {
 | 
						|
		return isTable, err
 | 
						|
	}
 | 
						|
	switch rv.Kind() {
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		b.body = strconv.AppendInt(b.body, rv.Int(), 10)
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
						|
		b.body = strconv.AppendUint(b.body, rv.Uint(), 10)
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		b.body = strconv.AppendFloat(b.body, rv.Float(), 'e', -1, 64)
 | 
						|
	case reflect.Bool:
 | 
						|
		b.body = strconv.AppendBool(b.body, rv.Bool())
 | 
						|
	case reflect.String:
 | 
						|
		b.body = strconv.AppendQuote(b.body, rv.String())
 | 
						|
	case reflect.Ptr, reflect.Interface:
 | 
						|
		if rv.IsNil() {
 | 
						|
			return false, &marshalNilError{rv.Type()}
 | 
						|
		}
 | 
						|
		return b.value(cfg, rv.Elem(), name)
 | 
						|
	case reflect.Slice, reflect.Array:
 | 
						|
		rvlen := rv.Len()
 | 
						|
		if rvlen == 0 {
 | 
						|
			b.body = append(b.body, '[', ']')
 | 
						|
			return false, nil
 | 
						|
		}
 | 
						|
 | 
						|
		b.arrayDepth++
 | 
						|
		wroteElem := false
 | 
						|
		b.body = append(b.body, '[')
 | 
						|
		for i := 0; i < rvlen; i++ {
 | 
						|
			isTable, err := b.value(cfg, rv.Index(i), name)
 | 
						|
			if err != nil {
 | 
						|
				return isTable, err
 | 
						|
			}
 | 
						|
			wroteElem = wroteElem || !isTable
 | 
						|
			if wroteElem {
 | 
						|
				if i < rvlen-1 {
 | 
						|
					b.body = append(b.body, ',', ' ')
 | 
						|
				} else {
 | 
						|
					b.body = append(b.body, ']')
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if !wroteElem {
 | 
						|
			b.body = b.body[:len(b.body)-1] // rub out '['
 | 
						|
		}
 | 
						|
		b.arrayDepth--
 | 
						|
		return !wroteElem, nil
 | 
						|
	case reflect.Struct:
 | 
						|
		child := b.newChild(name)
 | 
						|
		err := child.structFields(cfg, rv)
 | 
						|
		b.addChild(child)
 | 
						|
		return true, err
 | 
						|
	case reflect.Map:
 | 
						|
		child := b.newChild(name)
 | 
						|
		err := child.mapFields(cfg, rv)
 | 
						|
		b.addChild(child)
 | 
						|
		return true, err
 | 
						|
	default:
 | 
						|
		return false, fmt.Errorf("toml: marshal: unsupported type %v", rv.Kind())
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 | 
						|
 | 
						|
func (b *tableBuf) marshaler(cfg *Config, rv reflect.Value, name string) (handled, isTable bool, err error) {
 | 
						|
	switch t := rv.Interface().(type) {
 | 
						|
	case encoding.TextMarshaler:
 | 
						|
		enc, err := t.MarshalText()
 | 
						|
		if err != nil {
 | 
						|
			return true, false, err
 | 
						|
		}
 | 
						|
		b.body = encodeTextMarshaler(b.body, string(enc))
 | 
						|
		return true, false, nil
 | 
						|
	case MarshalerRec:
 | 
						|
		newval, err := t.MarshalTOML()
 | 
						|
		if err != nil {
 | 
						|
			return true, false, err
 | 
						|
		}
 | 
						|
		isTable, err = b.value(cfg, reflect.ValueOf(newval), name)
 | 
						|
		return true, isTable, err
 | 
						|
	case Marshaler:
 | 
						|
		enc, err := t.MarshalTOML()
 | 
						|
		if err != nil {
 | 
						|
			return true, false, err
 | 
						|
		}
 | 
						|
		b.body = append(b.body, enc...)
 | 
						|
		return true, false, nil
 | 
						|
	}
 | 
						|
	return false, false, nil
 | 
						|
}
 | 
						|
 | 
						|
func encodeTextMarshaler(buf []byte, v string) []byte {
 | 
						|
	// Emit the value without quotes if possible.
 | 
						|
	if v == "true" || v == "false" {
 | 
						|
		return append(buf, v...)
 | 
						|
	} else if _, err := time.Parse(time.RFC3339Nano, v); err == nil {
 | 
						|
		return append(buf, v...)
 | 
						|
	} else if _, err := strconv.ParseInt(v, 10, 64); err == nil {
 | 
						|
		return append(buf, v...)
 | 
						|
	} else if _, err := strconv.ParseUint(v, 10, 64); err == nil {
 | 
						|
		return append(buf, v...)
 | 
						|
	} else if _, err := strconv.ParseFloat(v, 64); err == nil {
 | 
						|
		return append(buf, v...)
 | 
						|
	}
 | 
						|
	return strconv.AppendQuote(buf, v)
 | 
						|
}
 | 
						|
 | 
						|
func encodeMapKey(rv reflect.Value) (string, error) {
 | 
						|
	if rv.Kind() == reflect.String {
 | 
						|
		return rv.String(), nil
 | 
						|
	}
 | 
						|
	if tm, ok := rv.Interface().(encoding.TextMarshaler); ok {
 | 
						|
		b, err := tm.MarshalText()
 | 
						|
		return string(b), err
 | 
						|
	}
 | 
						|
	switch rv.Kind() {
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		return strconv.FormatInt(rv.Int(), 10), nil
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | 
						|
		return strconv.FormatUint(rv.Uint(), 10), nil
 | 
						|
	}
 | 
						|
	return "", fmt.Errorf("toml: invalid map key type %v", rv.Type())
 | 
						|
}
 | 
						|
 | 
						|
func isEmptyValue(v reflect.Value) bool {
 | 
						|
	switch v.Kind() {
 | 
						|
	case reflect.Array:
 | 
						|
		// encoding/json treats all arrays with non-zero length as non-empty. We check the
 | 
						|
		// array content here because zero-length arrays are almost never used.
 | 
						|
		len := v.Len()
 | 
						|
		for i := 0; i < len; i++ {
 | 
						|
			if !isEmptyValue(v.Index(i)) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return true
 | 
						|
	case reflect.Map, reflect.Slice, reflect.String:
 | 
						|
		return v.Len() == 0
 | 
						|
	case reflect.Bool:
 | 
						|
		return !v.Bool()
 | 
						|
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
						|
		return v.Int() == 0
 | 
						|
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 | 
						|
		return v.Uint() == 0
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		return v.Float() == 0
 | 
						|
	case reflect.Interface, reflect.Ptr:
 | 
						|
		return v.IsNil()
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
func quoteName(s string) string {
 | 
						|
	if len(s) == 0 {
 | 
						|
		return strconv.Quote(s)
 | 
						|
	}
 | 
						|
	for _, r := range s {
 | 
						|
		if r >= '0' && r <= '9' || r >= 'A' && r <= 'Z' || r >= 'a' && r <= 'z' || r == '-' || r == '_' {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		return strconv.Quote(s)
 | 
						|
	}
 | 
						|
	return s
 | 
						|
}
 |