Fix: Vendor missing libraries
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
ade0951d9d
commit
c83bd95be8
35 changed files with 5223 additions and 0 deletions
25
Godeps/Godeps.json
generated
25
Godeps/Godeps.json
generated
|
@ -26,6 +26,31 @@
|
||||||
"ImportPath": "github.com/gorilla/mux",
|
"ImportPath": "github.com/gorilla/mux",
|
||||||
"Rev": "49c024275504f0341e5a9971eb7ba7fa3dc7af40"
|
"Rev": "49c024275504f0341e5a9971eb7ba7fa3dc7af40"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/nicksnyder/go-i18n/i18n",
|
||||||
|
"Comment": "v1.8.1",
|
||||||
|
"Rev": "3e70a1a463008cea6726380c908b1a6a8bdf7b24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/nicksnyder/go-i18n/i18n/bundle",
|
||||||
|
"Comment": "v1.8.1",
|
||||||
|
"Rev": "3e70a1a463008cea6726380c908b1a6a8bdf7b24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/nicksnyder/go-i18n/i18n/language",
|
||||||
|
"Comment": "v1.8.1",
|
||||||
|
"Rev": "3e70a1a463008cea6726380c908b1a6a8bdf7b24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/nicksnyder/go-i18n/i18n/translation",
|
||||||
|
"Comment": "v1.8.1",
|
||||||
|
"Rev": "3e70a1a463008cea6726380c908b1a6a8bdf7b24"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/pelletier/go-toml",
|
||||||
|
"Comment": "v1.0.0-7-g69d355d",
|
||||||
|
"Rev": "69d355db5304c0f7f809a2edc054553e7142f016"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/satori/uuid",
|
"ImportPath": "github.com/satori/uuid",
|
||||||
"Comment": "v1.1.0-8-g5bf94b6",
|
"Comment": "v1.1.0-8-g5bf94b6",
|
||||||
|
|
19
vendor/github.com/nicksnyder/go-i18n/LICENSE
generated
vendored
Normal file
19
vendor/github.com/nicksnyder/go-i18n/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
Copyright (c) 2014 Nick Snyder https://github.com/nicksnyder
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
444
vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
generated
vendored
Normal file
444
vendor/github.com/nicksnyder/go-i18n/i18n/bundle/bundle.go
generated
vendored
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
// Package bundle manages translations for multiple languages.
|
||||||
|
package bundle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/translation"
|
||||||
|
toml "github.com/pelletier/go-toml"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TranslateFunc is a copy of i18n.TranslateFunc to avoid a circular dependency.
|
||||||
|
type TranslateFunc func(translationID string, args ...interface{}) string
|
||||||
|
|
||||||
|
// Bundle stores the translations for multiple languages.
|
||||||
|
type Bundle struct {
|
||||||
|
// The primary translations for a language tag and translation id.
|
||||||
|
translations map[string]map[string]translation.Translation
|
||||||
|
|
||||||
|
// Translations that can be used when an exact language match is not possible.
|
||||||
|
fallbackTranslations map[string]map[string]translation.Translation
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an empty bundle.
|
||||||
|
func New() *Bundle {
|
||||||
|
return &Bundle{
|
||||||
|
translations: make(map[string]map[string]translation.Translation),
|
||||||
|
fallbackTranslations: make(map[string]map[string]translation.Translation),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustLoadTranslationFile is similar to LoadTranslationFile
|
||||||
|
// except it panics if an error happens.
|
||||||
|
func (b *Bundle) MustLoadTranslationFile(filename string) {
|
||||||
|
if err := b.LoadTranslationFile(filename); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTranslationFile loads the translations from filename into memory.
|
||||||
|
//
|
||||||
|
// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
|
||||||
|
//
|
||||||
|
// Generally you should load translation files once during your program's initialization.
|
||||||
|
func (b *Bundle) LoadTranslationFile(filename string) error {
|
||||||
|
buf, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return b.ParseTranslationFileBytes(filename, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
|
||||||
|
//
|
||||||
|
// It is useful for parsing translation files embedded with go-bindata.
|
||||||
|
func (b *Bundle) ParseTranslationFileBytes(filename string, buf []byte) error {
|
||||||
|
basename := filepath.Base(filename)
|
||||||
|
langs := language.Parse(basename)
|
||||||
|
switch l := len(langs); {
|
||||||
|
case l == 0:
|
||||||
|
return fmt.Errorf("no language found in %q", basename)
|
||||||
|
case l > 1:
|
||||||
|
return fmt.Errorf("multiple languages found in filename %q: %v; expected one", basename, langs)
|
||||||
|
}
|
||||||
|
translations, err := parseTranslations(filename, buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.AddTranslation(langs[0], translations...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTranslations(filename string, buf []byte) ([]translation.Translation, error) {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return []translation.Translation{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := filepath.Ext(filename)
|
||||||
|
|
||||||
|
// `github.com/pelletier/go-toml` lacks an Unmarshal function,
|
||||||
|
// so we should parse TOML separately.
|
||||||
|
if ext == ".toml" {
|
||||||
|
tree, err := toml.LoadReader(bytes.NewReader(buf))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := make(map[string]map[string]interface{})
|
||||||
|
for k, v := range tree.ToMap() {
|
||||||
|
m[k] = v.(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFlatFormat(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then parse other formats.
|
||||||
|
if isStandardFormat(ext, buf) {
|
||||||
|
var standardFormat []map[string]interface{}
|
||||||
|
if err := unmarshal(ext, buf, &standardFormat); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal %v: %v", filename, err)
|
||||||
|
}
|
||||||
|
return parseStandardFormat(standardFormat)
|
||||||
|
} else {
|
||||||
|
var flatFormat map[string]map[string]interface{}
|
||||||
|
if err := unmarshal(ext, buf, &flatFormat); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal %v: %v", filename, err)
|
||||||
|
}
|
||||||
|
return parseFlatFormat(flatFormat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isStandardFormat(ext string, buf []byte) bool {
|
||||||
|
buf = deleteLeadingComments(ext, buf)
|
||||||
|
firstRune := rune(buf[0])
|
||||||
|
return (ext == ".json" && firstRune == '[') || (ext == ".yaml" && firstRune == '-')
|
||||||
|
}
|
||||||
|
|
||||||
|
// deleteLeadingComments deletes leading newlines and comments in buf.
|
||||||
|
// It only works for ext == ".yaml".
|
||||||
|
func deleteLeadingComments(ext string, buf []byte) []byte {
|
||||||
|
if ext != ".yaml" {
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
buf = bytes.TrimLeftFunc(buf, unicode.IsSpace)
|
||||||
|
if buf[0] == '#' {
|
||||||
|
buf = deleteLine(buf)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteLine(buf []byte) []byte {
|
||||||
|
index := bytes.IndexRune(buf, '\n')
|
||||||
|
if index == -1 { // If there is only one line without newline ...
|
||||||
|
return nil // ... delete it and return nothing.
|
||||||
|
}
|
||||||
|
if index == len(buf)-1 { // If there is only one line with newline ...
|
||||||
|
return nil // ... do the same as above.
|
||||||
|
}
|
||||||
|
return buf[index+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal finds an appropriate unmarshal function for ext
|
||||||
|
// (extension of filename) and unmarshals buf to out. out must be a pointer.
|
||||||
|
func unmarshal(ext string, buf []byte, out interface{}) error {
|
||||||
|
switch ext {
|
||||||
|
case ".json":
|
||||||
|
return json.Unmarshal(buf, out)
|
||||||
|
case ".yaml":
|
||||||
|
return yaml.Unmarshal(buf, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("unsupported file extension %v", ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStandardFormat(data []map[string]interface{}) ([]translation.Translation, error) {
|
||||||
|
translations := make([]translation.Translation, 0, len(data))
|
||||||
|
for i, translationData := range data {
|
||||||
|
t, err := translation.NewTranslation(translationData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse translation #%d because %s\n%v", i, err, translationData)
|
||||||
|
}
|
||||||
|
translations = append(translations, t)
|
||||||
|
}
|
||||||
|
return translations, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseFlatFormat just converts data from flat format to standard format
|
||||||
|
// and passes it to parseStandardFormat.
|
||||||
|
//
|
||||||
|
// Flat format logic:
|
||||||
|
// key of data must be a string and data[key] must be always map[string]interface{},
|
||||||
|
// but if there is only "other" key in it then it is non-plural, else plural.
|
||||||
|
func parseFlatFormat(data map[string]map[string]interface{}) ([]translation.Translation, error) {
|
||||||
|
var standardFormatData []map[string]interface{}
|
||||||
|
for id, translationData := range data {
|
||||||
|
dataObject := make(map[string]interface{})
|
||||||
|
dataObject["id"] = id
|
||||||
|
if len(translationData) == 1 { // non-plural form
|
||||||
|
_, otherExists := translationData["other"]
|
||||||
|
if otherExists {
|
||||||
|
dataObject["translation"] = translationData["other"]
|
||||||
|
}
|
||||||
|
} else { // plural form
|
||||||
|
dataObject["translation"] = translationData
|
||||||
|
}
|
||||||
|
|
||||||
|
standardFormatData = append(standardFormatData, dataObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseStandardFormat(standardFormatData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTranslation adds translations for a language.
|
||||||
|
//
|
||||||
|
// It is useful if your translations are in a format not supported by LoadTranslationFile.
|
||||||
|
func (b *Bundle) AddTranslation(lang *language.Language, translations ...translation.Translation) {
|
||||||
|
b.Lock()
|
||||||
|
defer b.Unlock()
|
||||||
|
if b.translations[lang.Tag] == nil {
|
||||||
|
b.translations[lang.Tag] = make(map[string]translation.Translation, len(translations))
|
||||||
|
}
|
||||||
|
currentTranslations := b.translations[lang.Tag]
|
||||||
|
for _, newTranslation := range translations {
|
||||||
|
if currentTranslation := currentTranslations[newTranslation.ID()]; currentTranslation != nil {
|
||||||
|
currentTranslations[newTranslation.ID()] = currentTranslation.Merge(newTranslation)
|
||||||
|
} else {
|
||||||
|
currentTranslations[newTranslation.ID()] = newTranslation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lang can provide translations for less specific language tags.
|
||||||
|
for _, tag := range lang.MatchingTags() {
|
||||||
|
b.fallbackTranslations[tag] = currentTranslations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translations returns all translations in the bundle.
|
||||||
|
func (b *Bundle) Translations() map[string]map[string]translation.Translation {
|
||||||
|
t := make(map[string]map[string]translation.Translation)
|
||||||
|
b.RLock()
|
||||||
|
for tag, translations := range b.translations {
|
||||||
|
t[tag] = make(map[string]translation.Translation)
|
||||||
|
for id, translation := range translations {
|
||||||
|
t[tag][id] = translation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.RUnlock()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageTags returns the tags of all languages that that have been added.
|
||||||
|
func (b *Bundle) LanguageTags() []string {
|
||||||
|
var tags []string
|
||||||
|
b.RLock()
|
||||||
|
for k := range b.translations {
|
||||||
|
tags = append(tags, k)
|
||||||
|
}
|
||||||
|
b.RUnlock()
|
||||||
|
return tags
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
|
||||||
|
func (b *Bundle) LanguageTranslationIDs(languageTag string) []string {
|
||||||
|
var ids []string
|
||||||
|
b.RLock()
|
||||||
|
for id := range b.translations[languageTag] {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
b.RUnlock()
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustTfunc is similar to Tfunc except it panics if an error happens.
|
||||||
|
func (b *Bundle) MustTfunc(pref string, prefs ...string) TranslateFunc {
|
||||||
|
tfunc, err := b.Tfunc(pref, prefs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tfunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
|
||||||
|
func (b *Bundle) MustTfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language) {
|
||||||
|
tfunc, language, err := b.TfuncAndLanguage(pref, prefs...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return tfunc, language
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tfunc is similar to TfuncAndLanguage except is doesn't return the Language.
|
||||||
|
func (b *Bundle) Tfunc(pref string, prefs ...string) (TranslateFunc, error) {
|
||||||
|
tfunc, _, err := b.TfuncAndLanguage(pref, prefs...)
|
||||||
|
return tfunc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TfuncAndLanguage returns a TranslateFunc for the first Language that
|
||||||
|
// has a non-zero number of translations in the bundle.
|
||||||
|
//
|
||||||
|
// The returned Language matches the the first language preference that could be satisfied,
|
||||||
|
// but this may not strictly match the language of the translations used to satisfy that preference.
|
||||||
|
//
|
||||||
|
// For example, the user may request "zh". If there are no translations for "zh" but there are translations
|
||||||
|
// for "zh-cn", then the translations for "zh-cn" will be used but the returned Language will be "zh".
|
||||||
|
//
|
||||||
|
// It can parse languages from Accept-Language headers (RFC 2616),
|
||||||
|
// but it assumes weights are monotonically decreasing.
|
||||||
|
func (b *Bundle) TfuncAndLanguage(pref string, prefs ...string) (TranslateFunc, *language.Language, error) {
|
||||||
|
lang := b.supportedLanguage(pref, prefs...)
|
||||||
|
var err error
|
||||||
|
if lang == nil {
|
||||||
|
err = fmt.Errorf("no supported languages found %#v", append(prefs, pref))
|
||||||
|
}
|
||||||
|
return func(translationID string, args ...interface{}) string {
|
||||||
|
return b.translate(lang, translationID, args...)
|
||||||
|
}, lang, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// supportedLanguage returns the first language which
|
||||||
|
// has a non-zero number of translations in the bundle.
|
||||||
|
func (b *Bundle) supportedLanguage(pref string, prefs ...string) *language.Language {
|
||||||
|
lang := b.translatedLanguage(pref)
|
||||||
|
if lang == nil {
|
||||||
|
for _, pref := range prefs {
|
||||||
|
lang = b.translatedLanguage(pref)
|
||||||
|
if lang != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lang
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bundle) translatedLanguage(src string) *language.Language {
|
||||||
|
langs := language.Parse(src)
|
||||||
|
b.RLock()
|
||||||
|
defer b.RUnlock()
|
||||||
|
for _, lang := range langs {
|
||||||
|
if len(b.translations[lang.Tag]) > 0 ||
|
||||||
|
len(b.fallbackTranslations[lang.Tag]) > 0 {
|
||||||
|
return lang
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bundle) translate(lang *language.Language, translationID string, args ...interface{}) string {
|
||||||
|
if lang == nil {
|
||||||
|
return translationID
|
||||||
|
}
|
||||||
|
|
||||||
|
translation := b.translation(lang, translationID)
|
||||||
|
if translation == nil {
|
||||||
|
return translationID
|
||||||
|
}
|
||||||
|
|
||||||
|
var data interface{}
|
||||||
|
var count interface{}
|
||||||
|
if argc := len(args); argc > 0 {
|
||||||
|
if isNumber(args[0]) {
|
||||||
|
count = args[0]
|
||||||
|
if argc > 1 {
|
||||||
|
data = args[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data = args[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count != nil {
|
||||||
|
if data == nil {
|
||||||
|
data = map[string]interface{}{"Count": count}
|
||||||
|
} else {
|
||||||
|
dataMap := toMap(data)
|
||||||
|
dataMap["Count"] = count
|
||||||
|
data = dataMap
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataMap := toMap(data)
|
||||||
|
if c, ok := dataMap["Count"]; ok {
|
||||||
|
count = c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p, _ := lang.Plural(count)
|
||||||
|
template := translation.Template(p)
|
||||||
|
if template == nil {
|
||||||
|
return translationID
|
||||||
|
}
|
||||||
|
|
||||||
|
s := template.Execute(data)
|
||||||
|
if s == "" {
|
||||||
|
return translationID
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bundle) translation(lang *language.Language, translationID string) translation.Translation {
|
||||||
|
b.RLock()
|
||||||
|
defer b.RUnlock()
|
||||||
|
translations := b.translations[lang.Tag]
|
||||||
|
if translations == nil {
|
||||||
|
translations = b.fallbackTranslations[lang.Tag]
|
||||||
|
if translations == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return translations[translationID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumber(n interface{}) bool {
|
||||||
|
switch n.(type) {
|
||||||
|
case int, int8, int16, int32, int64, string:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func toMap(input interface{}) map[string]interface{} {
|
||||||
|
if data, ok := input.(map[string]interface{}); ok {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
v := reflect.ValueOf(input)
|
||||||
|
switch v.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return toMap(v.Elem().Interface())
|
||||||
|
case reflect.Struct:
|
||||||
|
return structToMap(v)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts the top level of a struct to a map[string]interface{}.
|
||||||
|
// Code inspired by github.com/fatih/structs.
|
||||||
|
func structToMap(v reflect.Value) map[string]interface{} {
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
t := v.Type()
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
if field.PkgPath != "" {
|
||||||
|
// unexported field. skip.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[field.Name] = v.FieldByName(field.Name).Interface()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
158
vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
generated
vendored
Normal file
158
vendor/github.com/nicksnyder/go-i18n/i18n/i18n.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// Package i18n supports string translations with variable substitution and CLDR pluralization.
|
||||||
|
// It is intended to be used in conjunction with the goi18n command, although that is not strictly required.
|
||||||
|
//
|
||||||
|
// Initialization
|
||||||
|
//
|
||||||
|
// Your Go program should load translations during its initialization.
|
||||||
|
// i18n.MustLoadTranslationFile("path/to/fr-FR.all.json")
|
||||||
|
// If your translations are in a file format not supported by (Must)?LoadTranslationFile,
|
||||||
|
// then you can use the AddTranslation function to manually add translations.
|
||||||
|
//
|
||||||
|
// Fetching a translation
|
||||||
|
//
|
||||||
|
// Use Tfunc or MustTfunc to fetch a TranslateFunc that will return the translated string for a specific language.
|
||||||
|
// func handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// cookieLang := r.Cookie("lang")
|
||||||
|
// acceptLang := r.Header.Get("Accept-Language")
|
||||||
|
// defaultLang = "en-US" // known valid language
|
||||||
|
// T, err := i18n.Tfunc(cookieLang, acceptLang, defaultLang)
|
||||||
|
// fmt.Println(T("Hello world"))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Usually it is a good idea to identify strings by a generic id rather than the English translation,
|
||||||
|
// but the rest of this documentation will continue to use the English translation for readability.
|
||||||
|
// T("Hello world") // ok
|
||||||
|
// T("programGreeting") // better!
|
||||||
|
//
|
||||||
|
// Variables
|
||||||
|
//
|
||||||
|
// TranslateFunc supports strings that have variables using the text/template syntax.
|
||||||
|
// T("Hello {{.Person}}", map[string]interface{}{
|
||||||
|
// "Person": "Bob",
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// Pluralization
|
||||||
|
//
|
||||||
|
// TranslateFunc supports the pluralization of strings using the CLDR pluralization rules defined here:
|
||||||
|
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
|
||||||
|
// T("You have {{.Count}} unread emails.", 2)
|
||||||
|
// T("I am {{.Count}} meters tall.", "1.7")
|
||||||
|
//
|
||||||
|
// Plural strings may also have variables.
|
||||||
|
// T("{{.Person}} has {{.Count}} unread emails", 2, map[string]interface{}{
|
||||||
|
// "Person": "Bob",
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// Sentences with multiple plural components can be supported with nesting.
|
||||||
|
// T("{{.Person}} has {{.Count}} unread emails in the past {{.Timeframe}}.", 3, map[string]interface{}{
|
||||||
|
// "Person": "Bob",
|
||||||
|
// "Timeframe": T("{{.Count}} days", 2),
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// Templates
|
||||||
|
//
|
||||||
|
// You can use the .Funcs() method of a text/template or html/template to register a TranslateFunc
|
||||||
|
// for usage inside of that template.
|
||||||
|
package i18n
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/bundle"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/translation"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TranslateFunc returns the translation of the string identified by translationID.
|
||||||
|
//
|
||||||
|
// If there is no translation for translationID, then the translationID itself is returned.
|
||||||
|
// This makes it easy to identify missing translations in your app.
|
||||||
|
//
|
||||||
|
// If translationID is a non-plural form, then the first variadic argument may be a map[string]interface{}
|
||||||
|
// or struct that contains template data.
|
||||||
|
//
|
||||||
|
// If translationID is a plural form, the function accepts two parameter signatures
|
||||||
|
// 1. T(count int, data struct{})
|
||||||
|
// The first variadic argument must be an integer type
|
||||||
|
// (int, int8, int16, int32, int64) or a float formatted as a string (e.g. "123.45").
|
||||||
|
// The second variadic argument may be a map[string]interface{} or struct{} that contains template data.
|
||||||
|
// 2. T(data struct{})
|
||||||
|
// data must be a struct{} or map[string]interface{} that contains a Count field and the template data,
|
||||||
|
// Count field must be an integer type (int, int8, int16, int32, int64)
|
||||||
|
// or a float formatted as a string (e.g. "123.45").
|
||||||
|
type TranslateFunc func(translationID string, args ...interface{}) string
|
||||||
|
|
||||||
|
// IdentityTfunc returns a TranslateFunc that always returns the translationID passed to it.
|
||||||
|
//
|
||||||
|
// It is a useful placeholder when parsing a text/template or html/template
|
||||||
|
// before the actual Tfunc is available.
|
||||||
|
func IdentityTfunc() TranslateFunc {
|
||||||
|
return func(translationID string, args ...interface{}) string {
|
||||||
|
return translationID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultBundle = bundle.New()
|
||||||
|
|
||||||
|
// MustLoadTranslationFile is similar to LoadTranslationFile
|
||||||
|
// except it panics if an error happens.
|
||||||
|
func MustLoadTranslationFile(filename string) {
|
||||||
|
defaultBundle.MustLoadTranslationFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadTranslationFile loads the translations from filename into memory.
|
||||||
|
//
|
||||||
|
// The language that the translations are associated with is parsed from the filename (e.g. en-US.json).
|
||||||
|
//
|
||||||
|
// Generally you should load translation files once during your program's initialization.
|
||||||
|
func LoadTranslationFile(filename string) error {
|
||||||
|
return defaultBundle.LoadTranslationFile(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseTranslationFileBytes is similar to LoadTranslationFile except it parses the bytes in buf.
|
||||||
|
//
|
||||||
|
// It is useful for parsing translation files embedded with go-bindata.
|
||||||
|
func ParseTranslationFileBytes(filename string, buf []byte) error {
|
||||||
|
return defaultBundle.ParseTranslationFileBytes(filename, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTranslation adds translations for a language.
|
||||||
|
//
|
||||||
|
// It is useful if your translations are in a format not supported by LoadTranslationFile.
|
||||||
|
func AddTranslation(lang *language.Language, translations ...translation.Translation) {
|
||||||
|
defaultBundle.AddTranslation(lang, translations...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageTags returns the tags of all languages that have been added.
|
||||||
|
func LanguageTags() []string {
|
||||||
|
return defaultBundle.LanguageTags()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageTranslationIDs returns the ids of all translations that have been added for a given language.
|
||||||
|
func LanguageTranslationIDs(languageTag string) []string {
|
||||||
|
return defaultBundle.LanguageTranslationIDs(languageTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustTfunc is similar to Tfunc except it panics if an error happens.
|
||||||
|
func MustTfunc(languageSource string, languageSources ...string) TranslateFunc {
|
||||||
|
return TranslateFunc(defaultBundle.MustTfunc(languageSource, languageSources...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tfunc returns a TranslateFunc that will be bound to the first language which
|
||||||
|
// has a non-zero number of translations.
|
||||||
|
//
|
||||||
|
// It can parse languages from Accept-Language headers (RFC 2616).
|
||||||
|
func Tfunc(languageSource string, languageSources ...string) (TranslateFunc, error) {
|
||||||
|
tfunc, err := defaultBundle.Tfunc(languageSource, languageSources...)
|
||||||
|
return TranslateFunc(tfunc), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustTfuncAndLanguage is similar to TfuncAndLanguage except it panics if an error happens.
|
||||||
|
func MustTfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language) {
|
||||||
|
tfunc, lang := defaultBundle.MustTfuncAndLanguage(languageSource, languageSources...)
|
||||||
|
return TranslateFunc(tfunc), lang
|
||||||
|
}
|
||||||
|
|
||||||
|
// TfuncAndLanguage is similar to Tfunc except it also returns the language which TranslateFunc is bound to.
|
||||||
|
func TfuncAndLanguage(languageSource string, languageSources ...string) (TranslateFunc, *language.Language, error) {
|
||||||
|
tfunc, lang, err := defaultBundle.TfuncAndLanguage(languageSource, languageSources...)
|
||||||
|
return TranslateFunc(tfunc), lang, err
|
||||||
|
}
|
99
vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
generated
vendored
Normal file
99
vendor/github.com/nicksnyder/go-i18n/i18n/language/language.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Package language defines languages that implement CLDR pluralization.
|
||||||
|
package language
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Language is a written human language.
|
||||||
|
type Language struct {
|
||||||
|
// Tag uniquely identifies the language as defined by RFC 5646.
|
||||||
|
//
|
||||||
|
// Most language tags are a two character language code (ISO 639-1)
|
||||||
|
// optionally followed by a dash and a two character country code (ISO 3166-1).
|
||||||
|
// (e.g. en, pt-br)
|
||||||
|
Tag string
|
||||||
|
*PluralSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Language) String() string {
|
||||||
|
return l.Tag
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchingTags returns the set of language tags that map to this Language.
|
||||||
|
// e.g. "zh-hans-cn" yields {"zh", "zh-hans", "zh-hans-cn"}
|
||||||
|
// BUG: This should be computed once and stored as a field on Language for efficiency,
|
||||||
|
// but this would require changing how Languages are constructed.
|
||||||
|
func (l *Language) MatchingTags() []string {
|
||||||
|
parts := strings.Split(l.Tag, "-")
|
||||||
|
var prefix, matches []string
|
||||||
|
for _, part := range parts {
|
||||||
|
prefix = append(prefix, part)
|
||||||
|
match := strings.Join(prefix, "-")
|
||||||
|
matches = append(matches, match)
|
||||||
|
}
|
||||||
|
return matches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse returns a slice of supported languages found in src or nil if none are found.
|
||||||
|
// It can parse language tags and Accept-Language headers.
|
||||||
|
func Parse(src string) []*Language {
|
||||||
|
var langs []*Language
|
||||||
|
start := 0
|
||||||
|
for end, chr := range src {
|
||||||
|
switch chr {
|
||||||
|
case ',', ';', '.':
|
||||||
|
tag := strings.TrimSpace(src[start:end])
|
||||||
|
if spec := getPluralSpec(tag); spec != nil {
|
||||||
|
langs = append(langs, &Language{NormalizeTag(tag), spec})
|
||||||
|
}
|
||||||
|
start = end + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start > 0 {
|
||||||
|
tag := strings.TrimSpace(src[start:])
|
||||||
|
if spec := getPluralSpec(tag); spec != nil {
|
||||||
|
langs = append(langs, &Language{NormalizeTag(tag), spec})
|
||||||
|
}
|
||||||
|
return dedupe(langs)
|
||||||
|
}
|
||||||
|
if spec := getPluralSpec(src); spec != nil {
|
||||||
|
langs = append(langs, &Language{NormalizeTag(src), spec})
|
||||||
|
}
|
||||||
|
return langs
|
||||||
|
}
|
||||||
|
|
||||||
|
func dedupe(langs []*Language) []*Language {
|
||||||
|
found := make(map[string]struct{}, len(langs))
|
||||||
|
deduped := make([]*Language, 0, len(langs))
|
||||||
|
for _, lang := range langs {
|
||||||
|
if _, ok := found[lang.Tag]; !ok {
|
||||||
|
found[lang.Tag] = struct{}{}
|
||||||
|
deduped = append(deduped, lang)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deduped
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse is similar to Parse except it panics instead of retuning a nil Language.
|
||||||
|
func MustParse(src string) []*Language {
|
||||||
|
langs := Parse(src)
|
||||||
|
if len(langs) == 0 {
|
||||||
|
panic(fmt.Errorf("unable to parse language from %q", src))
|
||||||
|
}
|
||||||
|
return langs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds support for a new language.
|
||||||
|
func Add(l *Language) {
|
||||||
|
tag := NormalizeTag(l.Tag)
|
||||||
|
pluralSpecs[tag] = l.PluralSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NormalizeTag returns a language tag with all lower-case characters
|
||||||
|
// and dashes "-" instead of underscores "_"
|
||||||
|
func NormalizeTag(tag string) string {
|
||||||
|
tag = strings.ToLower(tag)
|
||||||
|
return strings.Replace(tag, "_", "-", -1)
|
||||||
|
}
|
119
vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
generated
vendored
Normal file
119
vendor/github.com/nicksnyder/go-i18n/i18n/language/operands.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
package language
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
|
||||||
|
type operands struct {
|
||||||
|
N float64 // absolute value of the source number (integer and decimals)
|
||||||
|
I int64 // integer digits of n
|
||||||
|
V int64 // number of visible fraction digits in n, with trailing zeros
|
||||||
|
W int64 // number of visible fraction digits in n, without trailing zeros
|
||||||
|
F int64 // visible fractional digits in n, with trailing zeros
|
||||||
|
T int64 // visible fractional digits in n, without trailing zeros
|
||||||
|
}
|
||||||
|
|
||||||
|
// NmodEqualAny returns true if o represents an integer equal to any of the arguments.
|
||||||
|
func (o *operands) NequalsAny(any ...int64) bool {
|
||||||
|
for _, i := range any {
|
||||||
|
if o.I == i && o.T == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NmodEqualAny returns true if o represents an integer equal to any of the arguments modulo mod.
|
||||||
|
func (o *operands) NmodEqualsAny(mod int64, any ...int64) bool {
|
||||||
|
modI := o.I % mod
|
||||||
|
for _, i := range any {
|
||||||
|
if modI == i && o.T == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NmodInRange returns true if o represents an integer in the closed interval [from, to].
|
||||||
|
func (o *operands) NinRange(from, to int64) bool {
|
||||||
|
return o.T == 0 && from <= o.I && o.I <= to
|
||||||
|
}
|
||||||
|
|
||||||
|
// NmodInRange returns true if o represents an integer in the closed interval [from, to] modulo mod.
|
||||||
|
func (o *operands) NmodInRange(mod, from, to int64) bool {
|
||||||
|
modI := o.I % mod
|
||||||
|
return o.T == 0 && from <= modI && modI <= to
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOperands(v interface{}) (*operands, error) {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case int:
|
||||||
|
return newOperandsInt64(int64(v)), nil
|
||||||
|
case int8:
|
||||||
|
return newOperandsInt64(int64(v)), nil
|
||||||
|
case int16:
|
||||||
|
return newOperandsInt64(int64(v)), nil
|
||||||
|
case int32:
|
||||||
|
return newOperandsInt64(int64(v)), nil
|
||||||
|
case int64:
|
||||||
|
return newOperandsInt64(v), nil
|
||||||
|
case string:
|
||||||
|
return newOperandsString(v)
|
||||||
|
case float32, float64:
|
||||||
|
return nil, fmt.Errorf("floats should be formatted into a string")
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid type %T; expected integer or string", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOperandsInt64(i int64) *operands {
|
||||||
|
if i < 0 {
|
||||||
|
i = -i
|
||||||
|
}
|
||||||
|
return &operands{float64(i), i, 0, 0, 0, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOperandsString(s string) (*operands, error) {
|
||||||
|
if s[0] == '-' {
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
n, err := strconv.ParseFloat(s, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ops := &operands{N: n}
|
||||||
|
parts := strings.SplitN(s, ".", 2)
|
||||||
|
ops.I, err = strconv.ParseInt(parts[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(parts) == 1 {
|
||||||
|
return ops, nil
|
||||||
|
}
|
||||||
|
fraction := parts[1]
|
||||||
|
ops.V = int64(len(fraction))
|
||||||
|
for i := ops.V - 1; i >= 0; i-- {
|
||||||
|
if fraction[i] != '0' {
|
||||||
|
ops.W = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ops.V > 0 {
|
||||||
|
f, err := strconv.ParseInt(fraction, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ops.F = f
|
||||||
|
}
|
||||||
|
if ops.W > 0 {
|
||||||
|
t, err := strconv.ParseInt(fraction[:ops.W], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ops.T = t
|
||||||
|
}
|
||||||
|
return ops, nil
|
||||||
|
}
|
40
vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
generated
vendored
Normal file
40
vendor/github.com/nicksnyder/go-i18n/i18n/language/plural.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package language
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Plural represents a language pluralization form as defined here:
|
||||||
|
// http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||||
|
type Plural string
|
||||||
|
|
||||||
|
// All defined plural categories.
|
||||||
|
const (
|
||||||
|
Invalid Plural = "invalid"
|
||||||
|
Zero = "zero"
|
||||||
|
One = "one"
|
||||||
|
Two = "two"
|
||||||
|
Few = "few"
|
||||||
|
Many = "many"
|
||||||
|
Other = "other"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPlural returns src as a Plural
|
||||||
|
// or Invalid and a non-nil error if src is not a valid Plural.
|
||||||
|
func NewPlural(src string) (Plural, error) {
|
||||||
|
switch src {
|
||||||
|
case "zero":
|
||||||
|
return Zero, nil
|
||||||
|
case "one":
|
||||||
|
return One, nil
|
||||||
|
case "two":
|
||||||
|
return Two, nil
|
||||||
|
case "few":
|
||||||
|
return Few, nil
|
||||||
|
case "many":
|
||||||
|
return Many, nil
|
||||||
|
case "other":
|
||||||
|
return Other, nil
|
||||||
|
}
|
||||||
|
return Invalid, fmt.Errorf("invalid plural category %s", src)
|
||||||
|
}
|
74
vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
generated
vendored
Normal file
74
vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package language
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// PluralSpec defines the CLDR plural rules for a language.
|
||||||
|
// http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html
|
||||||
|
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
|
||||||
|
type PluralSpec struct {
|
||||||
|
Plurals map[Plural]struct{}
|
||||||
|
PluralFunc func(*operands) Plural
|
||||||
|
}
|
||||||
|
|
||||||
|
var pluralSpecs = make(map[string]*PluralSpec)
|
||||||
|
|
||||||
|
func normalizePluralSpecID(id string) string {
|
||||||
|
id = strings.Replace(id, "_", "-", -1)
|
||||||
|
id = strings.ToLower(id)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerPluralSpec(ids []string, ps *PluralSpec) {
|
||||||
|
for _, id := range ids {
|
||||||
|
id = normalizePluralSpecID(id)
|
||||||
|
pluralSpecs[id] = ps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plural returns the plural category for number as defined by
|
||||||
|
// the language's CLDR plural rules.
|
||||||
|
func (ps *PluralSpec) Plural(number interface{}) (Plural, error) {
|
||||||
|
ops, err := newOperands(number)
|
||||||
|
if err != nil {
|
||||||
|
return Invalid, err
|
||||||
|
}
|
||||||
|
return ps.PluralFunc(ops), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPluralSpec returns the PluralSpec that matches the longest prefix of tag.
|
||||||
|
// It returns nil if no PluralSpec matches tag.
|
||||||
|
func getPluralSpec(tag string) *PluralSpec {
|
||||||
|
tag = NormalizeTag(tag)
|
||||||
|
subtag := tag
|
||||||
|
for {
|
||||||
|
if spec := pluralSpecs[subtag]; spec != nil {
|
||||||
|
return spec
|
||||||
|
}
|
||||||
|
end := strings.LastIndex(subtag, "-")
|
||||||
|
if end == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
subtag = subtag[:end]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPluralSet(plurals ...Plural) map[Plural]struct{} {
|
||||||
|
set := make(map[Plural]struct{}, len(plurals))
|
||||||
|
for _, plural := range plurals {
|
||||||
|
set[plural] = struct{}{}
|
||||||
|
}
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
func intInRange(i, from, to int64) bool {
|
||||||
|
return from <= i && i <= to
|
||||||
|
}
|
||||||
|
|
||||||
|
func intEqualsAny(i int64, any ...int64) bool {
|
||||||
|
for _, a := range any {
|
||||||
|
if i == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
567
vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
generated
vendored
Normal file
567
vendor/github.com/nicksnyder/go-i18n/i18n/language/pluralspec_gen.go
generated
vendored
Normal file
|
@ -0,0 +1,567 @@
|
||||||
|
package language
|
||||||
|
|
||||||
|
// This file is generated by i18n/language/codegen/generate.sh
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
registerPluralSpec([]string{"bm", "bo", "dz", "id", "ig", "ii", "in", "ja", "jbo", "jv", "jw", "kde", "kea", "km", "ko", "lkt", "lo", "ms", "my", "nqo", "root", "sah", "ses", "sg", "th", "to", "vi", "wo", "yo", "zh"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"am", "as", "bn", "fa", "gu", "hi", "kn", "mr", "zu"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 0 or n = 1
|
||||||
|
if intEqualsAny(ops.I, 0) ||
|
||||||
|
ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ff", "fr", "hy", "kab"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 0,1
|
||||||
|
if intEqualsAny(ops.I, 0, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ast", "ca", "de", "en", "et", "fi", "fy", "gl", "it", "ji", "nl", "sv", "sw", "ur", "yi"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 1 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"si"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0,1 or i = 0 and f = 1
|
||||||
|
if ops.NequalsAny(0, 1) ||
|
||||||
|
intEqualsAny(ops.I, 0) && intEqualsAny(ops.F, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ak", "bh", "guw", "ln", "mg", "nso", "pa", "ti", "wa"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0..1
|
||||||
|
if ops.NinRange(0, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"tzm"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0..1 or n = 11..99
|
||||||
|
if ops.NinRange(0, 1) ||
|
||||||
|
ops.NinRange(11, 99) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"pt"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0..2 and n != 2
|
||||||
|
if ops.NinRange(0, 2) && !ops.NequalsAny(2) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"af", "asa", "az", "bem", "bez", "bg", "brx", "ce", "cgg", "chr", "ckb", "dv", "ee", "el", "eo", "es", "eu", "fo", "fur", "gsw", "ha", "haw", "hu", "jgo", "jmc", "ka", "kaj", "kcg", "kk", "kkj", "kl", "ks", "ksb", "ku", "ky", "lb", "lg", "mas", "mgo", "ml", "mn", "nah", "nb", "nd", "ne", "nn", "nnh", "no", "nr", "ny", "nyn", "om", "or", "os", "pap", "ps", "rm", "rof", "rwk", "saq", "sdh", "seh", "sn", "so", "sq", "ss", "ssy", "st", "syr", "ta", "te", "teo", "tig", "tk", "tn", "tr", "ts", "ug", "uz", "ve", "vo", "vun", "wae", "xh", "xog"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"pt_PT"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1 and v = 0
|
||||||
|
if ops.NequalsAny(1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"da"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1 or t != 0 and i = 0,1
|
||||||
|
if ops.NequalsAny(1) ||
|
||||||
|
!intEqualsAny(ops.T, 0) && intEqualsAny(ops.I, 0, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"is"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0
|
||||||
|
if intEqualsAny(ops.T, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
|
||||||
|
!intEqualsAny(ops.T, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"mk"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 10 = 1 or f % 10 = 1
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) ||
|
||||||
|
intEqualsAny(ops.F%10, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"fil", "tl"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i = 1,2,3 or v = 0 and i % 10 != 4,6,9 or v != 0 and f % 10 != 4,6,9
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I, 1, 2, 3) ||
|
||||||
|
intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I%10, 4, 6, 9) ||
|
||||||
|
!intEqualsAny(ops.V, 0) && !intEqualsAny(ops.F%10, 4, 6, 9) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"lv", "prg"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Zero, One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19
|
||||||
|
if ops.NmodEqualsAny(10, 0) ||
|
||||||
|
ops.NmodInRange(100, 11, 19) ||
|
||||||
|
intEqualsAny(ops.V, 2) && intInRange(ops.F%100, 11, 19) {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
// n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1
|
||||||
|
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) ||
|
||||||
|
intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) ||
|
||||||
|
!intEqualsAny(ops.V, 2) && intEqualsAny(ops.F%10, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"lag"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Zero, One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0
|
||||||
|
if ops.NequalsAny(0) {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
// i = 0,1 and n != 0
|
||||||
|
if intEqualsAny(ops.I, 0, 1) && !ops.NequalsAny(0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ksh"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Zero, One, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0
|
||||||
|
if ops.NequalsAny(0) {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"iu", "kw", "naq", "se", "sma", "smi", "smj", "smn", "sms"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2
|
||||||
|
if ops.NequalsAny(2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"shi"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 0 or n = 1
|
||||||
|
if intEqualsAny(ops.I, 0) ||
|
||||||
|
ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2..10
|
||||||
|
if ops.NinRange(2, 10) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"mo", "ro"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 1 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v != 0 or n = 0 or n != 1 and n % 100 = 1..19
|
||||||
|
if !intEqualsAny(ops.V, 0) ||
|
||||||
|
ops.NequalsAny(0) ||
|
||||||
|
!ops.NequalsAny(1) && ops.NmodInRange(100, 1, 19) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"bs", "hr", "sh", "sr"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) ||
|
||||||
|
intEqualsAny(ops.F%10, 1) && !intEqualsAny(ops.F%100, 11) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14
|
||||||
|
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) ||
|
||||||
|
intInRange(ops.F%10, 2, 4) && !intInRange(ops.F%100, 12, 14) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"gd"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1,11
|
||||||
|
if ops.NequalsAny(1, 11) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2,12
|
||||||
|
if ops.NequalsAny(2, 12) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// n = 3..10,13..19
|
||||||
|
if ops.NinRange(3, 10) || ops.NinRange(13, 19) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"sl"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 100 = 1
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 100 = 2
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// v = 0 and i % 100 = 3..4 or v != 0
|
||||||
|
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
|
||||||
|
!intEqualsAny(ops.V, 0) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"dsb", "hsb"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 100 = 1 or f % 100 = 1
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 1) ||
|
||||||
|
intEqualsAny(ops.F%100, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 100 = 2 or f % 100 = 2
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 2) ||
|
||||||
|
intEqualsAny(ops.F%100, 2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// v = 0 and i % 100 = 3..4 or f % 100 = 3..4
|
||||||
|
if intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 3, 4) ||
|
||||||
|
intInRange(ops.F%100, 3, 4) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"he", "iw"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 1 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// i = 2 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 2) && intEqualsAny(ops.V, 0) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// v = 0 and n != 0..10 and n % 10 = 0
|
||||||
|
if intEqualsAny(ops.V, 0) && !ops.NinRange(0, 10) && ops.NmodEqualsAny(10, 0) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"cs", "sk"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 1 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// i = 2..4 and v = 0
|
||||||
|
if intInRange(ops.I, 2, 4) && intEqualsAny(ops.V, 0) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// v != 0
|
||||||
|
if !intEqualsAny(ops.V, 0) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"pl"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// i = 1 and v = 0
|
||||||
|
if intEqualsAny(ops.I, 1) && intEqualsAny(ops.V, 0) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
|
||||||
|
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14
|
||||||
|
if intEqualsAny(ops.V, 0) && !intEqualsAny(ops.I, 1) && intInRange(ops.I%10, 0, 1) ||
|
||||||
|
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
|
||||||
|
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 12, 14) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"be"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n % 10 = 1 and n % 100 != 11
|
||||||
|
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n % 10 = 2..4 and n % 100 != 12..14
|
||||||
|
if ops.NmodInRange(10, 2, 4) && !ops.NmodInRange(100, 12, 14) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14
|
||||||
|
if ops.NmodEqualsAny(10, 0) ||
|
||||||
|
ops.NmodInRange(10, 5, 9) ||
|
||||||
|
ops.NmodInRange(100, 11, 14) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"lt"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n % 10 = 1 and n % 100 != 11..19
|
||||||
|
if ops.NmodEqualsAny(10, 1) && !ops.NmodInRange(100, 11, 19) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n % 10 = 2..9 and n % 100 != 11..19
|
||||||
|
if ops.NmodInRange(10, 2, 9) && !ops.NmodInRange(100, 11, 19) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// f != 0
|
||||||
|
if !intEqualsAny(ops.F, 0) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"mt"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 0 or n % 100 = 2..10
|
||||||
|
if ops.NequalsAny(0) ||
|
||||||
|
ops.NmodInRange(100, 2, 10) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n % 100 = 11..19
|
||||||
|
if ops.NmodInRange(100, 11, 19) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ru", "uk"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 10 = 1 and i % 100 != 11
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) && !intEqualsAny(ops.I%100, 11) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 10 = 2..4 and i % 100 != 12..14
|
||||||
|
if intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 2, 4) && !intInRange(ops.I%100, 12, 14) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 0) ||
|
||||||
|
intEqualsAny(ops.V, 0) && intInRange(ops.I%10, 5, 9) ||
|
||||||
|
intEqualsAny(ops.V, 0) && intInRange(ops.I%100, 11, 14) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"br"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n % 10 = 1 and n % 100 != 11,71,91
|
||||||
|
if ops.NmodEqualsAny(10, 1) && !ops.NmodEqualsAny(100, 11, 71, 91) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n % 10 = 2 and n % 100 != 12,72,92
|
||||||
|
if ops.NmodEqualsAny(10, 2) && !ops.NmodEqualsAny(100, 12, 72, 92) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99
|
||||||
|
if (ops.NmodInRange(10, 3, 4) || ops.NmodEqualsAny(10, 9)) && !(ops.NmodInRange(100, 10, 19) || ops.NmodInRange(100, 70, 79) || ops.NmodInRange(100, 90, 99)) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n != 0 and n % 1000000 = 0
|
||||||
|
if !ops.NequalsAny(0) && ops.NmodEqualsAny(1000000, 0) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ga"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2
|
||||||
|
if ops.NequalsAny(2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// n = 3..6
|
||||||
|
if ops.NinRange(3, 6) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n = 7..10
|
||||||
|
if ops.NinRange(7, 10) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"gv"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(One, Two, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// v = 0 and i % 10 = 1
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// v = 0 and i % 10 = 2
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%10, 2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// v = 0 and i % 100 = 0,20,40,60,80
|
||||||
|
if intEqualsAny(ops.V, 0) && intEqualsAny(ops.I%100, 0, 20, 40, 60, 80) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// v != 0
|
||||||
|
if !intEqualsAny(ops.V, 0) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"ar"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0
|
||||||
|
if ops.NequalsAny(0) {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2
|
||||||
|
if ops.NequalsAny(2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// n % 100 = 3..10
|
||||||
|
if ops.NmodInRange(100, 3, 10) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n % 100 = 11..99
|
||||||
|
if ops.NmodInRange(100, 11, 99) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
registerPluralSpec([]string{"cy"}, &PluralSpec{
|
||||||
|
Plurals: newPluralSet(Zero, One, Two, Few, Many, Other),
|
||||||
|
PluralFunc: func(ops *operands) Plural {
|
||||||
|
// n = 0
|
||||||
|
if ops.NequalsAny(0) {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
// n = 1
|
||||||
|
if ops.NequalsAny(1) {
|
||||||
|
return One
|
||||||
|
}
|
||||||
|
// n = 2
|
||||||
|
if ops.NequalsAny(2) {
|
||||||
|
return Two
|
||||||
|
}
|
||||||
|
// n = 3
|
||||||
|
if ops.NequalsAny(3) {
|
||||||
|
return Few
|
||||||
|
}
|
||||||
|
// n = 6
|
||||||
|
if ops.NequalsAny(6) {
|
||||||
|
return Many
|
||||||
|
}
|
||||||
|
return Other
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
82
vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go
generated
vendored
Normal file
82
vendor/github.com/nicksnyder/go-i18n/i18n/translation/plural_translation.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package translation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pluralTranslation struct {
|
||||||
|
id string
|
||||||
|
templates map[language.Plural]*template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) MarshalInterface() interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"id": pt.id,
|
||||||
|
"translation": pt.templates,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) MarshalFlatInterface() interface{} {
|
||||||
|
return pt.templates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) ID() string {
|
||||||
|
return pt.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) Template(pc language.Plural) *template {
|
||||||
|
return pt.templates[pc]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) UntranslatedCopy() Translation {
|
||||||
|
return &pluralTranslation{pt.id, make(map[language.Plural]*template)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) Normalize(l *language.Language) Translation {
|
||||||
|
// Delete plural categories that don't belong to this language.
|
||||||
|
for pc := range pt.templates {
|
||||||
|
if _, ok := l.Plurals[pc]; !ok {
|
||||||
|
delete(pt.templates, pc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create map entries for missing valid categories.
|
||||||
|
for pc := range l.Plurals {
|
||||||
|
if _, ok := pt.templates[pc]; !ok {
|
||||||
|
pt.templates[pc] = mustNewTemplate("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) Backfill(src Translation) Translation {
|
||||||
|
for pc, t := range pt.templates {
|
||||||
|
if t == nil || t.src == "" {
|
||||||
|
pt.templates[pc] = src.Template(language.Other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) Merge(t Translation) Translation {
|
||||||
|
other, ok := t.(*pluralTranslation)
|
||||||
|
if !ok || pt.ID() != t.ID() {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
for pluralCategory, template := range other.templates {
|
||||||
|
if template != nil && template.src != "" {
|
||||||
|
pt.templates[pluralCategory] = template
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *pluralTranslation) Incomplete(l *language.Language) bool {
|
||||||
|
for pc := range l.Plurals {
|
||||||
|
if t := pt.templates[pc]; t == nil || t.src == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Translation(&pluralTranslation{})
|
61
vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go
generated
vendored
Normal file
61
vendor/github.com/nicksnyder/go-i18n/i18n/translation/single_translation.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package translation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type singleTranslation struct {
|
||||||
|
id string
|
||||||
|
template *template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) MarshalInterface() interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"id": st.id,
|
||||||
|
"translation": st.template,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) MarshalFlatInterface() interface{} {
|
||||||
|
return map[string]interface{}{"other": st.template}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) ID() string {
|
||||||
|
return st.id
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) Template(pc language.Plural) *template {
|
||||||
|
return st.template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) UntranslatedCopy() Translation {
|
||||||
|
return &singleTranslation{st.id, mustNewTemplate("")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) Normalize(language *language.Language) Translation {
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) Backfill(src Translation) Translation {
|
||||||
|
if st.template == nil || st.template.src == "" {
|
||||||
|
st.template = src.Template(language.Other)
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) Merge(t Translation) Translation {
|
||||||
|
other, ok := t.(*singleTranslation)
|
||||||
|
if !ok || st.ID() != t.ID() {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
if other.template != nil && other.template.src != "" {
|
||||||
|
st.template = other.template
|
||||||
|
}
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *singleTranslation) Incomplete(l *language.Language) bool {
|
||||||
|
return st.template == nil || st.template.src == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Translation(&singleTranslation{})
|
65
vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
generated
vendored
Normal file
65
vendor/github.com/nicksnyder/go-i18n/i18n/translation/template.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package translation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"strings"
|
||||||
|
gotemplate "text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
type template struct {
|
||||||
|
tmpl *gotemplate.Template
|
||||||
|
src string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTemplate(src string) (*template, error) {
|
||||||
|
if src == "" {
|
||||||
|
return new(template), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var tmpl template
|
||||||
|
err := tmpl.parseTemplate(src)
|
||||||
|
return &tmpl, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustNewTemplate(src string) *template {
|
||||||
|
t, err := newTemplate(src)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *template) String() string {
|
||||||
|
return t.src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *template) Execute(args interface{}) string {
|
||||||
|
if t.tmpl == nil {
|
||||||
|
return t.src
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := t.tmpl.Execute(&buf, args); err != nil {
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *template) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(t.src), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *template) UnmarshalText(src []byte) error {
|
||||||
|
return t.parseTemplate(string(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *template) parseTemplate(src string) (err error) {
|
||||||
|
t.src = src
|
||||||
|
if strings.Contains(src, "{{") {
|
||||||
|
t.tmpl, err = gotemplate.New(src).Parse(src)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = encoding.TextMarshaler(&template{})
|
||||||
|
var _ = encoding.TextUnmarshaler(&template{})
|
84
vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
generated
vendored
Normal file
84
vendor/github.com/nicksnyder/go-i18n/i18n/translation/translation.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
// Package translation defines the interface for a translation.
|
||||||
|
package translation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Translation is the interface that represents a translated string.
|
||||||
|
type Translation interface {
|
||||||
|
// MarshalInterface returns the object that should be used
|
||||||
|
// to serialize the translation.
|
||||||
|
MarshalInterface() interface{}
|
||||||
|
MarshalFlatInterface() interface{}
|
||||||
|
ID() string
|
||||||
|
Template(language.Plural) *template
|
||||||
|
UntranslatedCopy() Translation
|
||||||
|
Normalize(language *language.Language) Translation
|
||||||
|
Backfill(src Translation) Translation
|
||||||
|
Merge(Translation) Translation
|
||||||
|
Incomplete(l *language.Language) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// SortableByID implements sort.Interface for a slice of translations.
|
||||||
|
type SortableByID []Translation
|
||||||
|
|
||||||
|
func (a SortableByID) Len() int { return len(a) }
|
||||||
|
func (a SortableByID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
func (a SortableByID) Less(i, j int) bool { return a[i].ID() < a[j].ID() }
|
||||||
|
|
||||||
|
// NewTranslation reflects on data to create a new Translation.
|
||||||
|
//
|
||||||
|
// data["id"] must be a string and data["translation"] must be either a string
|
||||||
|
// for a non-plural translation or a map[string]interface{} for a plural translation.
|
||||||
|
func NewTranslation(data map[string]interface{}) (Translation, error) {
|
||||||
|
id, ok := data["id"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`missing "id" key`)
|
||||||
|
}
|
||||||
|
var pluralObject map[string]interface{}
|
||||||
|
switch translation := data["translation"].(type) {
|
||||||
|
case string:
|
||||||
|
tmpl, err := newTemplate(translation)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &singleTranslation{id, tmpl}, nil
|
||||||
|
case map[interface{}]interface{}:
|
||||||
|
// The YAML parser uses interface{} keys so we first convert them to string keys.
|
||||||
|
pluralObject = make(map[string]interface{})
|
||||||
|
for k, v := range translation {
|
||||||
|
kStr, ok := k.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`invalid plural category type %T; expected string`, k)
|
||||||
|
}
|
||||||
|
pluralObject[kStr] = v
|
||||||
|
}
|
||||||
|
case map[string]interface{}:
|
||||||
|
pluralObject = translation
|
||||||
|
case nil:
|
||||||
|
return nil, fmt.Errorf(`missing "translation" key`)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(`unsupported type for "translation" key %T`, translation)
|
||||||
|
}
|
||||||
|
|
||||||
|
templates := make(map[language.Plural]*template, len(pluralObject))
|
||||||
|
for k, v := range pluralObject {
|
||||||
|
pc, err := language.NewPlural(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
str, ok := v.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf(`plural category "%s" has value of type %T; expected string`, pc, v)
|
||||||
|
}
|
||||||
|
tmpl, err := newTemplate(str)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
templates[pc] = tmpl
|
||||||
|
}
|
||||||
|
return &pluralTranslation{id, templates}, nil
|
||||||
|
}
|
1
vendor/github.com/pelletier/go-toml/.gitignore
generated
vendored
Normal file
1
vendor/github.com/pelletier/go-toml/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
test_program/test_program_bin
|
23
vendor/github.com/pelletier/go-toml/.travis.yml
generated
vendored
Normal file
23
vendor/github.com/pelletier/go-toml/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
sudo: false
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.6.4
|
||||||
|
- 1.7.6
|
||||||
|
- 1.8.3
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
fast_finish: true
|
||||||
|
script:
|
||||||
|
- if [ -n "$(go fmt ./...)" ]; then exit 1; fi
|
||||||
|
- ./test.sh
|
||||||
|
- ./benchmark.sh $TRAVIS_BRANCH https://github.com/$TRAVIS_REPO_SLUG.git
|
||||||
|
before_install:
|
||||||
|
- go get github.com/axw/gocov/gocov
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||||
|
branches:
|
||||||
|
only: [master]
|
||||||
|
after_success:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci -coverprofile=coverage.out -repotoken $COVERALLS_TOKEN
|
21
vendor/github.com/pelletier/go-toml/LICENSE
generated
vendored
Normal file
21
vendor/github.com/pelletier/go-toml/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013 - 2017 Thomas Pelletier, Eric Anderton
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
118
vendor/github.com/pelletier/go-toml/README.md
generated
vendored
Normal file
118
vendor/github.com/pelletier/go-toml/README.md
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
# go-toml
|
||||||
|
|
||||||
|
Go library for the [TOML](https://github.com/mojombo/toml) format.
|
||||||
|
|
||||||
|
This library supports TOML version
|
||||||
|
[v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md)
|
||||||
|
|
||||||
|
[![GoDoc](https://godoc.org/github.com/pelletier/go-toml?status.svg)](http://godoc.org/github.com/pelletier/go-toml)
|
||||||
|
[![license](https://img.shields.io/github/license/pelletier/go-toml.svg)](https://github.com/pelletier/go-toml/blob/master/LICENSE)
|
||||||
|
[![Build Status](https://travis-ci.org/pelletier/go-toml.svg?branch=master)](https://travis-ci.org/pelletier/go-toml)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/pelletier/go-toml/badge.svg?branch=master)](https://coveralls.io/github/pelletier/go-toml?branch=master)
|
||||||
|
[![Go Report Card](https://goreportcard.com/badge/github.com/pelletier/go-toml)](https://goreportcard.com/report/github.com/pelletier/go-toml)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Go-toml provides the following features for using data parsed from TOML documents:
|
||||||
|
|
||||||
|
* Load TOML documents from files and string data
|
||||||
|
* Easily navigate TOML structure using Tree
|
||||||
|
* Mashaling and unmarshaling to and from data structures
|
||||||
|
* Line & column position data for all parsed elements
|
||||||
|
* [Query support similar to JSON-Path](query/)
|
||||||
|
* Syntax errors contain line and column numbers
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/pelletier/go-toml"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage example
|
||||||
|
|
||||||
|
Read a TOML document:
|
||||||
|
|
||||||
|
```go
|
||||||
|
config, _ := toml.LoadString(`
|
||||||
|
[postgres]
|
||||||
|
user = "pelletier"
|
||||||
|
password = "mypassword"`)
|
||||||
|
// retrieve data directly
|
||||||
|
user := config.Get("postgres.user").(string)
|
||||||
|
|
||||||
|
// or using an intermediate object
|
||||||
|
postgresConfig := config.Get("postgres").(*toml.Tree)
|
||||||
|
password = postgresConfig.Get("password").(string)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use Unmarshal:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Postgres struct {
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
type Config struct {
|
||||||
|
Postgres Postgres
|
||||||
|
}
|
||||||
|
|
||||||
|
doc := []byte(`
|
||||||
|
[postgres]
|
||||||
|
user = "pelletier"
|
||||||
|
password = "mypassword"`)
|
||||||
|
|
||||||
|
config := Config{}
|
||||||
|
Unmarshal(doc, &config)
|
||||||
|
fmt.Println("user=", config.Postgres.User)
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use a query:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// use a query to gather elements without walking the tree
|
||||||
|
results, _ := config.Query("$..[user,password]")
|
||||||
|
for ii, item := range results.Values() {
|
||||||
|
fmt.Println("Query result %d: %v", ii, item)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The documentation and additional examples are available at
|
||||||
|
[godoc.org](http://godoc.org/github.com/pelletier/go-toml).
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Go-toml provides two handy command line tools:
|
||||||
|
|
||||||
|
* `tomll`: Reads TOML files and lint them.
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/pelletier/go-toml/cmd/tomll
|
||||||
|
tomll --help
|
||||||
|
```
|
||||||
|
* `tomljson`: Reads a TOML file and outputs its JSON representation.
|
||||||
|
|
||||||
|
```
|
||||||
|
go install github.com/pelletier/go-toml/cmd/tomljson
|
||||||
|
tomljson --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contribute
|
||||||
|
|
||||||
|
Feel free to report bugs and patches using GitHub's pull requests system on
|
||||||
|
[pelletier/go-toml](https://github.com/pelletier/go-toml). Any feedback would be
|
||||||
|
much appreciated!
|
||||||
|
|
||||||
|
### Run tests
|
||||||
|
|
||||||
|
You have to make sure two kind of tests run:
|
||||||
|
|
||||||
|
1. The Go unit tests
|
||||||
|
2. The TOML examples base
|
||||||
|
|
||||||
|
You can run both of them using `./test.sh`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
The MIT License (MIT). Read [LICENSE](LICENSE).
|
164
vendor/github.com/pelletier/go-toml/benchmark.json
generated
vendored
Normal file
164
vendor/github.com/pelletier/go-toml/benchmark.json
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
{
|
||||||
|
"array": {
|
||||||
|
"key1": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"key2": [
|
||||||
|
"red",
|
||||||
|
"yellow",
|
||||||
|
"green"
|
||||||
|
],
|
||||||
|
"key3": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"key4": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"c"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"key5": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"key6": [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"boolean": {
|
||||||
|
"False": false,
|
||||||
|
"True": true
|
||||||
|
},
|
||||||
|
"datetime": {
|
||||||
|
"key1": "1979-05-27T07:32:00Z",
|
||||||
|
"key2": "1979-05-27T00:32:00-07:00",
|
||||||
|
"key3": "1979-05-27T00:32:00.999999-07:00"
|
||||||
|
},
|
||||||
|
"float": {
|
||||||
|
"both": {
|
||||||
|
"key": 6.626e-34
|
||||||
|
},
|
||||||
|
"exponent": {
|
||||||
|
"key1": 5e+22,
|
||||||
|
"key2": 1000000,
|
||||||
|
"key3": -0.02
|
||||||
|
},
|
||||||
|
"fractional": {
|
||||||
|
"key1": 1,
|
||||||
|
"key2": 3.1415,
|
||||||
|
"key3": -0.01
|
||||||
|
},
|
||||||
|
"underscores": {
|
||||||
|
"key1": 9224617.445991227,
|
||||||
|
"key2": 1e+100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fruit": [{
|
||||||
|
"name": "apple",
|
||||||
|
"physical": {
|
||||||
|
"color": "red",
|
||||||
|
"shape": "round"
|
||||||
|
},
|
||||||
|
"variety": [{
|
||||||
|
"name": "red delicious"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "granny smith"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "banana",
|
||||||
|
"variety": [{
|
||||||
|
"name": "plantain"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"integer": {
|
||||||
|
"key1": 99,
|
||||||
|
"key2": 42,
|
||||||
|
"key3": 0,
|
||||||
|
"key4": -17,
|
||||||
|
"underscores": {
|
||||||
|
"key1": 1000,
|
||||||
|
"key2": 5349221,
|
||||||
|
"key3": 12345
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"products": [{
|
||||||
|
"name": "Hammer",
|
||||||
|
"sku": 738594937
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"color": "gray",
|
||||||
|
"name": "Nail",
|
||||||
|
"sku": 284758393
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"string": {
|
||||||
|
"basic": {
|
||||||
|
"basic": "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
|
||||||
|
},
|
||||||
|
"literal": {
|
||||||
|
"multiline": {
|
||||||
|
"lines": "The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n",
|
||||||
|
"regex2": "I [dw]on't need \\d{2} apples"
|
||||||
|
},
|
||||||
|
"quoted": "Tom \"Dubs\" Preston-Werner",
|
||||||
|
"regex": "\u003c\\i\\c*\\s*\u003e",
|
||||||
|
"winpath": "C:\\Users\\nodejs\\templates",
|
||||||
|
"winpath2": "\\\\ServerX\\admin$\\system32\\"
|
||||||
|
},
|
||||||
|
"multiline": {
|
||||||
|
"continued": {
|
||||||
|
"key1": "The quick brown fox jumps over the lazy dog.",
|
||||||
|
"key2": "The quick brown fox jumps over the lazy dog.",
|
||||||
|
"key3": "The quick brown fox jumps over the lazy dog."
|
||||||
|
},
|
||||||
|
"key1": "One\nTwo",
|
||||||
|
"key2": "One\nTwo",
|
||||||
|
"key3": "One\nTwo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"inline": {
|
||||||
|
"name": {
|
||||||
|
"first": "Tom",
|
||||||
|
"last": "Preston-Werner"
|
||||||
|
},
|
||||||
|
"point": {
|
||||||
|
"x": 1,
|
||||||
|
"y": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"key": "value",
|
||||||
|
"subtable": {
|
||||||
|
"key": "another value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"y": {
|
||||||
|
"z": {
|
||||||
|
"w": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
vendor/github.com/pelletier/go-toml/benchmark.sh
generated
vendored
Executable file
32
vendor/github.com/pelletier/go-toml/benchmark.sh
generated
vendored
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
reference_ref=${1:-master}
|
||||||
|
reference_git=${2:-.}
|
||||||
|
|
||||||
|
if ! `hash benchstat 2>/dev/null`; then
|
||||||
|
echo "Installing benchstat"
|
||||||
|
go get golang.org/x/perf/cmd/benchstat
|
||||||
|
go install golang.org/x/perf/cmd/benchstat
|
||||||
|
fi
|
||||||
|
|
||||||
|
tempdir=`mktemp -d /tmp/go-toml-benchmark-XXXXXX`
|
||||||
|
ref_tempdir="${tempdir}/ref"
|
||||||
|
ref_benchmark="${ref_tempdir}/benchmark-`echo -n ${reference_ref}|tr -s '/' '-'`.txt"
|
||||||
|
local_benchmark="`pwd`/benchmark-local.txt"
|
||||||
|
|
||||||
|
echo "=== ${reference_ref} (${ref_tempdir})"
|
||||||
|
git clone ${reference_git} ${ref_tempdir} >/dev/null 2>/dev/null
|
||||||
|
pushd ${ref_tempdir} >/dev/null
|
||||||
|
git checkout ${reference_ref} >/dev/null 2>/dev/null
|
||||||
|
go test -bench=. -benchmem | tee ${ref_benchmark}
|
||||||
|
popd >/dev/null
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== local"
|
||||||
|
go test -bench=. -benchmem | tee ${local_benchmark}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== diff"
|
||||||
|
benchstat -delta-test=none ${ref_benchmark} ${local_benchmark}
|
244
vendor/github.com/pelletier/go-toml/benchmark.toml
generated
vendored
Normal file
244
vendor/github.com/pelletier/go-toml/benchmark.toml
generated
vendored
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
################################################################################
|
||||||
|
## Comment
|
||||||
|
|
||||||
|
# Speak your mind with the hash symbol. They go from the symbol to the end of
|
||||||
|
# the line.
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Table
|
||||||
|
|
||||||
|
# Tables (also known as hash tables or dictionaries) are collections of
|
||||||
|
# key/value pairs. They appear in square brackets on a line by themselves.
|
||||||
|
|
||||||
|
[table]
|
||||||
|
|
||||||
|
key = "value" # Yeah, you can do this.
|
||||||
|
|
||||||
|
# Nested tables are denoted by table names with dots in them. Name your tables
|
||||||
|
# whatever crap you please, just don't use #, ., [ or ].
|
||||||
|
|
||||||
|
[table.subtable]
|
||||||
|
|
||||||
|
key = "another value"
|
||||||
|
|
||||||
|
# You don't need to specify all the super-tables if you don't want to. TOML
|
||||||
|
# knows how to do it for you.
|
||||||
|
|
||||||
|
# [x] you
|
||||||
|
# [x.y] don't
|
||||||
|
# [x.y.z] need these
|
||||||
|
[x.y.z.w] # for this to work
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Inline Table
|
||||||
|
|
||||||
|
# Inline tables provide a more compact syntax for expressing tables. They are
|
||||||
|
# especially useful for grouped data that can otherwise quickly become verbose.
|
||||||
|
# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
|
||||||
|
# allowed between the curly braces unless they are valid within a value.
|
||||||
|
|
||||||
|
[table.inline]
|
||||||
|
|
||||||
|
name = { first = "Tom", last = "Preston-Werner" }
|
||||||
|
point = { x = 1, y = 2 }
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## String
|
||||||
|
|
||||||
|
# There are four ways to express strings: basic, multi-line basic, literal, and
|
||||||
|
# multi-line literal. All strings must contain only valid UTF-8 characters.
|
||||||
|
|
||||||
|
[string.basic]
|
||||||
|
|
||||||
|
basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
|
||||||
|
|
||||||
|
[string.multiline]
|
||||||
|
|
||||||
|
# The following strings are byte-for-byte equivalent:
|
||||||
|
key1 = "One\nTwo"
|
||||||
|
key2 = """One\nTwo"""
|
||||||
|
key3 = """
|
||||||
|
One
|
||||||
|
Two"""
|
||||||
|
|
||||||
|
[string.multiline.continued]
|
||||||
|
|
||||||
|
# The following strings are byte-for-byte equivalent:
|
||||||
|
key1 = "The quick brown fox jumps over the lazy dog."
|
||||||
|
|
||||||
|
key2 = """
|
||||||
|
The quick brown \
|
||||||
|
|
||||||
|
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog."""
|
||||||
|
|
||||||
|
key3 = """\
|
||||||
|
The quick brown \
|
||||||
|
fox jumps over \
|
||||||
|
the lazy dog.\
|
||||||
|
"""
|
||||||
|
|
||||||
|
[string.literal]
|
||||||
|
|
||||||
|
# What you see is what you get.
|
||||||
|
winpath = 'C:\Users\nodejs\templates'
|
||||||
|
winpath2 = '\\ServerX\admin$\system32\'
|
||||||
|
quoted = 'Tom "Dubs" Preston-Werner'
|
||||||
|
regex = '<\i\c*\s*>'
|
||||||
|
|
||||||
|
|
||||||
|
[string.literal.multiline]
|
||||||
|
|
||||||
|
regex2 = '''I [dw]on't need \d{2} apples'''
|
||||||
|
lines = '''
|
||||||
|
The first newline is
|
||||||
|
trimmed in raw strings.
|
||||||
|
All other whitespace
|
||||||
|
is preserved.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Integer
|
||||||
|
|
||||||
|
# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
|
||||||
|
# Negative numbers are prefixed with a minus sign.
|
||||||
|
|
||||||
|
[integer]
|
||||||
|
|
||||||
|
key1 = +99
|
||||||
|
key2 = 42
|
||||||
|
key3 = 0
|
||||||
|
key4 = -17
|
||||||
|
|
||||||
|
[integer.underscores]
|
||||||
|
|
||||||
|
# For large numbers, you may use underscores to enhance readability. Each
|
||||||
|
# underscore must be surrounded by at least one digit.
|
||||||
|
key1 = 1_000
|
||||||
|
key2 = 5_349_221
|
||||||
|
key3 = 1_2_3_4_5 # valid but inadvisable
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Float
|
||||||
|
|
||||||
|
# A float consists of an integer part (which may be prefixed with a plus or
|
||||||
|
# minus sign) followed by a fractional part and/or an exponent part.
|
||||||
|
|
||||||
|
[float.fractional]
|
||||||
|
|
||||||
|
key1 = +1.0
|
||||||
|
key2 = 3.1415
|
||||||
|
key3 = -0.01
|
||||||
|
|
||||||
|
[float.exponent]
|
||||||
|
|
||||||
|
key1 = 5e+22
|
||||||
|
key2 = 1e6
|
||||||
|
key3 = -2E-2
|
||||||
|
|
||||||
|
[float.both]
|
||||||
|
|
||||||
|
key = 6.626e-34
|
||||||
|
|
||||||
|
[float.underscores]
|
||||||
|
|
||||||
|
key1 = 9_224_617.445_991_228_313
|
||||||
|
key2 = 1e1_00
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Boolean
|
||||||
|
|
||||||
|
# Booleans are just the tokens you're used to. Always lowercase.
|
||||||
|
|
||||||
|
[boolean]
|
||||||
|
|
||||||
|
True = true
|
||||||
|
False = false
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Datetime
|
||||||
|
|
||||||
|
# Datetimes are RFC 3339 dates.
|
||||||
|
|
||||||
|
[datetime]
|
||||||
|
|
||||||
|
key1 = 1979-05-27T07:32:00Z
|
||||||
|
key2 = 1979-05-27T00:32:00-07:00
|
||||||
|
key3 = 1979-05-27T00:32:00.999999-07:00
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Array
|
||||||
|
|
||||||
|
# Arrays are square brackets with other primitives inside. Whitespace is
|
||||||
|
# ignored. Elements are separated by commas. Data types may not be mixed.
|
||||||
|
|
||||||
|
[array]
|
||||||
|
|
||||||
|
key1 = [ 1, 2, 3 ]
|
||||||
|
key2 = [ "red", "yellow", "green" ]
|
||||||
|
key3 = [ [ 1, 2 ], [3, 4, 5] ]
|
||||||
|
#key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
|
||||||
|
|
||||||
|
# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
|
||||||
|
# also ignore newlines between the brackets. Terminating commas are ok before
|
||||||
|
# the closing bracket.
|
||||||
|
|
||||||
|
key5 = [
|
||||||
|
1, 2, 3
|
||||||
|
]
|
||||||
|
key6 = [
|
||||||
|
1,
|
||||||
|
2, # this is ok
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
## Array of Tables
|
||||||
|
|
||||||
|
# These can be expressed by using a table name in double brackets. Each table
|
||||||
|
# with the same double bracketed name will be an element in the array. The
|
||||||
|
# tables are inserted in the order encountered.
|
||||||
|
|
||||||
|
[[products]]
|
||||||
|
|
||||||
|
name = "Hammer"
|
||||||
|
sku = 738594937
|
||||||
|
|
||||||
|
[[products]]
|
||||||
|
|
||||||
|
[[products]]
|
||||||
|
|
||||||
|
name = "Nail"
|
||||||
|
sku = 284758393
|
||||||
|
color = "gray"
|
||||||
|
|
||||||
|
|
||||||
|
# You can create nested arrays of tables as well.
|
||||||
|
|
||||||
|
[[fruit]]
|
||||||
|
name = "apple"
|
||||||
|
|
||||||
|
[fruit.physical]
|
||||||
|
color = "red"
|
||||||
|
shape = "round"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "red delicious"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "granny smith"
|
||||||
|
|
||||||
|
[[fruit]]
|
||||||
|
name = "banana"
|
||||||
|
|
||||||
|
[[fruit.variety]]
|
||||||
|
name = "plantain"
|
121
vendor/github.com/pelletier/go-toml/benchmark.yml
generated
vendored
Normal file
121
vendor/github.com/pelletier/go-toml/benchmark.yml
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
---
|
||||||
|
array:
|
||||||
|
key1:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
key2:
|
||||||
|
- red
|
||||||
|
- yellow
|
||||||
|
- green
|
||||||
|
key3:
|
||||||
|
- - 1
|
||||||
|
- 2
|
||||||
|
- - 3
|
||||||
|
- 4
|
||||||
|
- 5
|
||||||
|
key4:
|
||||||
|
- - 1
|
||||||
|
- 2
|
||||||
|
- - a
|
||||||
|
- b
|
||||||
|
- c
|
||||||
|
key5:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
key6:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
boolean:
|
||||||
|
'False': false
|
||||||
|
'True': true
|
||||||
|
datetime:
|
||||||
|
key1: '1979-05-27T07:32:00Z'
|
||||||
|
key2: '1979-05-27T00:32:00-07:00'
|
||||||
|
key3: '1979-05-27T00:32:00.999999-07:00'
|
||||||
|
float:
|
||||||
|
both:
|
||||||
|
key: 6.626e-34
|
||||||
|
exponent:
|
||||||
|
key1: 5.0e+22
|
||||||
|
key2: 1000000
|
||||||
|
key3: -0.02
|
||||||
|
fractional:
|
||||||
|
key1: 1
|
||||||
|
key2: 3.1415
|
||||||
|
key3: -0.01
|
||||||
|
underscores:
|
||||||
|
key1: 9224617.445991227
|
||||||
|
key2: 1.0e+100
|
||||||
|
fruit:
|
||||||
|
- name: apple
|
||||||
|
physical:
|
||||||
|
color: red
|
||||||
|
shape: round
|
||||||
|
variety:
|
||||||
|
- name: red delicious
|
||||||
|
- name: granny smith
|
||||||
|
- name: banana
|
||||||
|
variety:
|
||||||
|
- name: plantain
|
||||||
|
integer:
|
||||||
|
key1: 99
|
||||||
|
key2: 42
|
||||||
|
key3: 0
|
||||||
|
key4: -17
|
||||||
|
underscores:
|
||||||
|
key1: 1000
|
||||||
|
key2: 5349221
|
||||||
|
key3: 12345
|
||||||
|
products:
|
||||||
|
- name: Hammer
|
||||||
|
sku: 738594937
|
||||||
|
- {}
|
||||||
|
- color: gray
|
||||||
|
name: Nail
|
||||||
|
sku: 284758393
|
||||||
|
string:
|
||||||
|
basic:
|
||||||
|
basic: "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."
|
||||||
|
literal:
|
||||||
|
multiline:
|
||||||
|
lines: |
|
||||||
|
The first newline is
|
||||||
|
trimmed in raw strings.
|
||||||
|
All other whitespace
|
||||||
|
is preserved.
|
||||||
|
regex2: I [dw]on't need \d{2} apples
|
||||||
|
quoted: Tom "Dubs" Preston-Werner
|
||||||
|
regex: "<\\i\\c*\\s*>"
|
||||||
|
winpath: C:\Users\nodejs\templates
|
||||||
|
winpath2: "\\\\ServerX\\admin$\\system32\\"
|
||||||
|
multiline:
|
||||||
|
continued:
|
||||||
|
key1: The quick brown fox jumps over the lazy dog.
|
||||||
|
key2: The quick brown fox jumps over the lazy dog.
|
||||||
|
key3: The quick brown fox jumps over the lazy dog.
|
||||||
|
key1: |-
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
key2: |-
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
key3: |-
|
||||||
|
One
|
||||||
|
Two
|
||||||
|
table:
|
||||||
|
inline:
|
||||||
|
name:
|
||||||
|
first: Tom
|
||||||
|
last: Preston-Werner
|
||||||
|
point:
|
||||||
|
x: 1
|
||||||
|
y: 2
|
||||||
|
key: value
|
||||||
|
subtable:
|
||||||
|
key: another value
|
||||||
|
x:
|
||||||
|
y:
|
||||||
|
z:
|
||||||
|
w: {}
|
23
vendor/github.com/pelletier/go-toml/doc.go
generated
vendored
Normal file
23
vendor/github.com/pelletier/go-toml/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Package toml is a TOML parser and manipulation library.
|
||||||
|
//
|
||||||
|
// This version supports the specification as described in
|
||||||
|
// https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md
|
||||||
|
//
|
||||||
|
// Marshaling
|
||||||
|
//
|
||||||
|
// Go-toml can marshal and unmarshal TOML documents from and to data
|
||||||
|
// structures.
|
||||||
|
//
|
||||||
|
// TOML document as a tree
|
||||||
|
//
|
||||||
|
// Go-toml can operate on a TOML document as a tree. Use one of the Load*
|
||||||
|
// functions to parse TOML data and obtain a Tree instance, then one of its
|
||||||
|
// methods to manipulate the tree.
|
||||||
|
//
|
||||||
|
// JSONPath-like queries
|
||||||
|
//
|
||||||
|
// The package github.com/pelletier/go-toml/query implements a system
|
||||||
|
// similar to JSONPath to quickly retrive elements of a TOML document using a
|
||||||
|
// single expression. See the package documentation for more information.
|
||||||
|
//
|
||||||
|
package toml
|
29
vendor/github.com/pelletier/go-toml/example-crlf.toml
generated
vendored
Normal file
29
vendor/github.com/pelletier/go-toml/example-crlf.toml
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This is a TOML document. Boom.
|
||||||
|
|
||||||
|
title = "TOML Example"
|
||||||
|
|
||||||
|
[owner]
|
||||||
|
name = "Tom Preston-Werner"
|
||||||
|
organization = "GitHub"
|
||||||
|
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||||
|
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||||
|
|
||||||
|
[database]
|
||||||
|
server = "192.168.1.1"
|
||||||
|
ports = [ 8001, 8001, 8002 ]
|
||||||
|
connection_max = 5000
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||||
|
[servers.alpha]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[servers.beta]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[clients]
|
||||||
|
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
29
vendor/github.com/pelletier/go-toml/example.toml
generated
vendored
Normal file
29
vendor/github.com/pelletier/go-toml/example.toml
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This is a TOML document. Boom.
|
||||||
|
|
||||||
|
title = "TOML Example"
|
||||||
|
|
||||||
|
[owner]
|
||||||
|
name = "Tom Preston-Werner"
|
||||||
|
organization = "GitHub"
|
||||||
|
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
|
||||||
|
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
|
||||||
|
|
||||||
|
[database]
|
||||||
|
server = "192.168.1.1"
|
||||||
|
ports = [ 8001, 8001, 8002 ]
|
||||||
|
connection_max = 5000
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[servers]
|
||||||
|
|
||||||
|
# You can indent as you please. Tabs or spaces. TOML don't care.
|
||||||
|
[servers.alpha]
|
||||||
|
ip = "10.0.0.1"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[servers.beta]
|
||||||
|
ip = "10.0.0.2"
|
||||||
|
dc = "eqdc10"
|
||||||
|
|
||||||
|
[clients]
|
||||||
|
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
|
94
vendor/github.com/pelletier/go-toml/keysparsing.go
generated
vendored
Normal file
94
vendor/github.com/pelletier/go-toml/keysparsing.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// Parsing keys handling both bare and quoted keys.
|
||||||
|
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseKey(key string) ([]string, error) {
|
||||||
|
groups := []string{}
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
inQuotes := false
|
||||||
|
wasInQuotes := false
|
||||||
|
escapeNext := false
|
||||||
|
ignoreSpace := true
|
||||||
|
expectDot := false
|
||||||
|
|
||||||
|
for _, char := range key {
|
||||||
|
if ignoreSpace {
|
||||||
|
if char == ' ' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ignoreSpace = false
|
||||||
|
}
|
||||||
|
if escapeNext {
|
||||||
|
buffer.WriteRune(char)
|
||||||
|
escapeNext = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch char {
|
||||||
|
case '\\':
|
||||||
|
escapeNext = true
|
||||||
|
continue
|
||||||
|
case '"':
|
||||||
|
if inQuotes {
|
||||||
|
groups = append(groups, buffer.String())
|
||||||
|
buffer.Reset()
|
||||||
|
wasInQuotes = true
|
||||||
|
}
|
||||||
|
inQuotes = !inQuotes
|
||||||
|
expectDot = false
|
||||||
|
case '.':
|
||||||
|
if inQuotes {
|
||||||
|
buffer.WriteRune(char)
|
||||||
|
} else {
|
||||||
|
if !wasInQuotes {
|
||||||
|
if buffer.Len() == 0 {
|
||||||
|
return nil, errors.New("empty table key")
|
||||||
|
}
|
||||||
|
groups = append(groups, buffer.String())
|
||||||
|
buffer.Reset()
|
||||||
|
}
|
||||||
|
ignoreSpace = true
|
||||||
|
expectDot = false
|
||||||
|
wasInQuotes = false
|
||||||
|
}
|
||||||
|
case ' ':
|
||||||
|
if inQuotes {
|
||||||
|
buffer.WriteRune(char)
|
||||||
|
} else {
|
||||||
|
expectDot = true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if !inQuotes && !isValidBareChar(char) {
|
||||||
|
return nil, fmt.Errorf("invalid bare character: %c", char)
|
||||||
|
}
|
||||||
|
if !inQuotes && expectDot {
|
||||||
|
return nil, errors.New("what?")
|
||||||
|
}
|
||||||
|
buffer.WriteRune(char)
|
||||||
|
expectDot = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inQuotes {
|
||||||
|
return nil, errors.New("mismatched quotes")
|
||||||
|
}
|
||||||
|
if escapeNext {
|
||||||
|
return nil, errors.New("unfinished escape sequence")
|
||||||
|
}
|
||||||
|
if buffer.Len() > 0 {
|
||||||
|
groups = append(groups, buffer.String())
|
||||||
|
}
|
||||||
|
if len(groups) == 0 {
|
||||||
|
return nil, errors.New("empty key")
|
||||||
|
}
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidBareChar(r rune) bool {
|
||||||
|
return isAlphanumeric(r) || r == '-' || unicode.IsNumber(r)
|
||||||
|
}
|
651
vendor/github.com/pelletier/go-toml/lexer.go
generated
vendored
Normal file
651
vendor/github.com/pelletier/go-toml/lexer.go
generated
vendored
Normal file
|
@ -0,0 +1,651 @@
|
||||||
|
// TOML lexer.
|
||||||
|
//
|
||||||
|
// Written using the principles developed by Rob Pike in
|
||||||
|
// http://www.youtube.com/watch?v=HxaD_trXwRE
|
||||||
|
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dateRegexp *regexp.Regexp
|
||||||
|
|
||||||
|
// Define state functions
|
||||||
|
type tomlLexStateFn func() tomlLexStateFn
|
||||||
|
|
||||||
|
// Define lexer
|
||||||
|
type tomlLexer struct {
|
||||||
|
inputIdx int
|
||||||
|
input []rune // Textual source
|
||||||
|
currentTokenStart int
|
||||||
|
currentTokenStop int
|
||||||
|
tokens []token
|
||||||
|
depth int
|
||||||
|
line int
|
||||||
|
col int
|
||||||
|
endbufferLine int
|
||||||
|
endbufferCol int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic read operations on input
|
||||||
|
|
||||||
|
func (l *tomlLexer) read() rune {
|
||||||
|
r := l.peek()
|
||||||
|
if r == '\n' {
|
||||||
|
l.endbufferLine++
|
||||||
|
l.endbufferCol = 1
|
||||||
|
} else {
|
||||||
|
l.endbufferCol++
|
||||||
|
}
|
||||||
|
l.inputIdx++
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) next() rune {
|
||||||
|
r := l.read()
|
||||||
|
|
||||||
|
if r != eof {
|
||||||
|
l.currentTokenStop++
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) ignore() {
|
||||||
|
l.currentTokenStart = l.currentTokenStop
|
||||||
|
l.line = l.endbufferLine
|
||||||
|
l.col = l.endbufferCol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) skip() {
|
||||||
|
l.next()
|
||||||
|
l.ignore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) fastForward(n int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) emitWithValue(t tokenType, value string) {
|
||||||
|
l.tokens = append(l.tokens, token{
|
||||||
|
Position: Position{l.line, l.col},
|
||||||
|
typ: t,
|
||||||
|
val: value,
|
||||||
|
})
|
||||||
|
l.ignore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) emit(t tokenType) {
|
||||||
|
l.emitWithValue(t, string(l.input[l.currentTokenStart:l.currentTokenStop]))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) peek() rune {
|
||||||
|
if l.inputIdx >= len(l.input) {
|
||||||
|
return eof
|
||||||
|
}
|
||||||
|
return l.input[l.inputIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) peekString(size int) string {
|
||||||
|
maxIdx := len(l.input)
|
||||||
|
upperIdx := l.inputIdx + size // FIXME: potential overflow
|
||||||
|
if upperIdx > maxIdx {
|
||||||
|
upperIdx = maxIdx
|
||||||
|
}
|
||||||
|
return string(l.input[l.inputIdx:upperIdx])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) follow(next string) bool {
|
||||||
|
return next == l.peekString(len(next))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error management
|
||||||
|
|
||||||
|
func (l *tomlLexer) errorf(format string, args ...interface{}) tomlLexStateFn {
|
||||||
|
l.tokens = append(l.tokens, token{
|
||||||
|
Position: Position{l.line, l.col},
|
||||||
|
typ: tokenError,
|
||||||
|
val: fmt.Sprintf(format, args...),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// State functions
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexVoid() tomlLexStateFn {
|
||||||
|
for {
|
||||||
|
next := l.peek()
|
||||||
|
switch next {
|
||||||
|
case '[':
|
||||||
|
return l.lexTableKey
|
||||||
|
case '#':
|
||||||
|
return l.lexComment(l.lexVoid)
|
||||||
|
case '=':
|
||||||
|
return l.lexEqual
|
||||||
|
case '\r':
|
||||||
|
fallthrough
|
||||||
|
case '\n':
|
||||||
|
l.skip()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSpace(next) {
|
||||||
|
l.skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.depth > 0 {
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isKeyStartChar(next) {
|
||||||
|
return l.lexKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if next == eof {
|
||||||
|
l.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.emit(tokenEOF)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexRvalue() tomlLexStateFn {
|
||||||
|
for {
|
||||||
|
next := l.peek()
|
||||||
|
switch next {
|
||||||
|
case '.':
|
||||||
|
return l.errorf("cannot start float with a dot")
|
||||||
|
case '=':
|
||||||
|
return l.lexEqual
|
||||||
|
case '[':
|
||||||
|
l.depth++
|
||||||
|
return l.lexLeftBracket
|
||||||
|
case ']':
|
||||||
|
l.depth--
|
||||||
|
return l.lexRightBracket
|
||||||
|
case '{':
|
||||||
|
return l.lexLeftCurlyBrace
|
||||||
|
case '}':
|
||||||
|
return l.lexRightCurlyBrace
|
||||||
|
case '#':
|
||||||
|
return l.lexComment(l.lexRvalue)
|
||||||
|
case '"':
|
||||||
|
return l.lexString
|
||||||
|
case '\'':
|
||||||
|
return l.lexLiteralString
|
||||||
|
case ',':
|
||||||
|
return l.lexComma
|
||||||
|
case '\r':
|
||||||
|
fallthrough
|
||||||
|
case '\n':
|
||||||
|
l.skip()
|
||||||
|
if l.depth == 0 {
|
||||||
|
return l.lexVoid
|
||||||
|
}
|
||||||
|
return l.lexRvalue
|
||||||
|
case '_':
|
||||||
|
return l.errorf("cannot start number with underscore")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.follow("true") {
|
||||||
|
return l.lexTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.follow("false") {
|
||||||
|
return l.lexFalse
|
||||||
|
}
|
||||||
|
|
||||||
|
if isSpace(next) {
|
||||||
|
l.skip()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if next == eof {
|
||||||
|
l.next()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleDate := l.peekString(35)
|
||||||
|
dateMatch := dateRegexp.FindString(possibleDate)
|
||||||
|
if dateMatch != "" {
|
||||||
|
l.fastForward(len(dateMatch))
|
||||||
|
return l.lexDate
|
||||||
|
}
|
||||||
|
|
||||||
|
if next == '+' || next == '-' || isDigit(next) {
|
||||||
|
return l.lexNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAlphanumeric(next) {
|
||||||
|
return l.lexKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.errorf("no value can start with %c", next)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.emit(tokenEOF)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenLeftCurlyBrace)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenRightCurlyBrace)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexDate() tomlLexStateFn {
|
||||||
|
l.emit(tokenDate)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexTrue() tomlLexStateFn {
|
||||||
|
l.fastForward(4)
|
||||||
|
l.emit(tokenTrue)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexFalse() tomlLexStateFn {
|
||||||
|
l.fastForward(5)
|
||||||
|
l.emit(tokenFalse)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexEqual() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenEqual)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexComma() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenComma)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexKey() tomlLexStateFn {
|
||||||
|
growingString := ""
|
||||||
|
|
||||||
|
for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() {
|
||||||
|
if r == '"' {
|
||||||
|
l.next()
|
||||||
|
str, err := l.lexStringAsString(`"`, false, true)
|
||||||
|
if err != nil {
|
||||||
|
return l.errorf(err.Error())
|
||||||
|
}
|
||||||
|
growingString += `"` + str + `"`
|
||||||
|
l.next()
|
||||||
|
continue
|
||||||
|
} else if r == '\n' {
|
||||||
|
return l.errorf("keys cannot contain new lines")
|
||||||
|
} else if isSpace(r) {
|
||||||
|
break
|
||||||
|
} else if !isValidBareChar(r) {
|
||||||
|
return l.errorf("keys cannot contain %c character", r)
|
||||||
|
}
|
||||||
|
growingString += string(r)
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
l.emitWithValue(tokenKey, growingString)
|
||||||
|
return l.lexVoid
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexComment(previousState tomlLexStateFn) tomlLexStateFn {
|
||||||
|
return func() tomlLexStateFn {
|
||||||
|
for next := l.peek(); next != '\n' && next != eof; next = l.peek() {
|
||||||
|
if next == '\r' && l.follow("\r\n") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
l.ignore()
|
||||||
|
return previousState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexLeftBracket() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenLeftBracket)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexLiteralStringAsString(terminator string, discardLeadingNewLine bool) (string, error) {
|
||||||
|
growingString := ""
|
||||||
|
|
||||||
|
if discardLeadingNewLine {
|
||||||
|
if l.follow("\r\n") {
|
||||||
|
l.skip()
|
||||||
|
l.skip()
|
||||||
|
} else if l.peek() == '\n' {
|
||||||
|
l.skip()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find end of string
|
||||||
|
for {
|
||||||
|
if l.follow(terminator) {
|
||||||
|
return growingString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
next := l.peek()
|
||||||
|
if next == eof {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
growingString += string(l.next())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("unclosed string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexLiteralString() tomlLexStateFn {
|
||||||
|
l.skip()
|
||||||
|
|
||||||
|
// handle special case for triple-quote
|
||||||
|
terminator := "'"
|
||||||
|
discardLeadingNewLine := false
|
||||||
|
if l.follow("''") {
|
||||||
|
l.skip()
|
||||||
|
l.skip()
|
||||||
|
terminator = "'''"
|
||||||
|
discardLeadingNewLine = true
|
||||||
|
}
|
||||||
|
|
||||||
|
str, err := l.lexLiteralStringAsString(terminator, discardLeadingNewLine)
|
||||||
|
if err != nil {
|
||||||
|
return l.errorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
l.emitWithValue(tokenString, str)
|
||||||
|
l.fastForward(len(terminator))
|
||||||
|
l.ignore()
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lex a string and return the results as a string.
|
||||||
|
// Terminator is the substring indicating the end of the token.
|
||||||
|
// The resulting string does not include the terminator.
|
||||||
|
func (l *tomlLexer) lexStringAsString(terminator string, discardLeadingNewLine, acceptNewLines bool) (string, error) {
|
||||||
|
growingString := ""
|
||||||
|
|
||||||
|
if discardLeadingNewLine {
|
||||||
|
if l.follow("\r\n") {
|
||||||
|
l.skip()
|
||||||
|
l.skip()
|
||||||
|
} else if l.peek() == '\n' {
|
||||||
|
l.skip()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if l.follow(terminator) {
|
||||||
|
return growingString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.follow("\\") {
|
||||||
|
l.next()
|
||||||
|
switch l.peek() {
|
||||||
|
case '\r':
|
||||||
|
fallthrough
|
||||||
|
case '\n':
|
||||||
|
fallthrough
|
||||||
|
case '\t':
|
||||||
|
fallthrough
|
||||||
|
case ' ':
|
||||||
|
// skip all whitespace chars following backslash
|
||||||
|
for strings.ContainsRune("\r\n\t ", l.peek()) {
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
case '"':
|
||||||
|
growingString += "\""
|
||||||
|
l.next()
|
||||||
|
case 'n':
|
||||||
|
growingString += "\n"
|
||||||
|
l.next()
|
||||||
|
case 'b':
|
||||||
|
growingString += "\b"
|
||||||
|
l.next()
|
||||||
|
case 'f':
|
||||||
|
growingString += "\f"
|
||||||
|
l.next()
|
||||||
|
case '/':
|
||||||
|
growingString += "/"
|
||||||
|
l.next()
|
||||||
|
case 't':
|
||||||
|
growingString += "\t"
|
||||||
|
l.next()
|
||||||
|
case 'r':
|
||||||
|
growingString += "\r"
|
||||||
|
l.next()
|
||||||
|
case '\\':
|
||||||
|
growingString += "\\"
|
||||||
|
l.next()
|
||||||
|
case 'u':
|
||||||
|
l.next()
|
||||||
|
code := ""
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
c := l.peek()
|
||||||
|
if !isHexDigit(c) {
|
||||||
|
return "", errors.New("unfinished unicode escape")
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
code = code + string(c)
|
||||||
|
}
|
||||||
|
intcode, err := strconv.ParseInt(code, 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("invalid unicode escape: \\u" + code)
|
||||||
|
}
|
||||||
|
growingString += string(rune(intcode))
|
||||||
|
case 'U':
|
||||||
|
l.next()
|
||||||
|
code := ""
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
c := l.peek()
|
||||||
|
if !isHexDigit(c) {
|
||||||
|
return "", errors.New("unfinished unicode escape")
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
code = code + string(c)
|
||||||
|
}
|
||||||
|
intcode, err := strconv.ParseInt(code, 16, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("invalid unicode escape: \\U" + code)
|
||||||
|
}
|
||||||
|
growingString += string(rune(intcode))
|
||||||
|
default:
|
||||||
|
return "", errors.New("invalid escape sequence: \\" + string(l.peek()))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r := l.peek()
|
||||||
|
|
||||||
|
if 0x00 <= r && r <= 0x1F && !(acceptNewLines && (r == '\n' || r == '\r')) {
|
||||||
|
return "", fmt.Errorf("unescaped control character %U", r)
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
growingString += string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.peek() == eof {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("unclosed string")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexString() tomlLexStateFn {
|
||||||
|
l.skip()
|
||||||
|
|
||||||
|
// handle special case for triple-quote
|
||||||
|
terminator := `"`
|
||||||
|
discardLeadingNewLine := false
|
||||||
|
acceptNewLines := false
|
||||||
|
if l.follow(`""`) {
|
||||||
|
l.skip()
|
||||||
|
l.skip()
|
||||||
|
terminator = `"""`
|
||||||
|
discardLeadingNewLine = true
|
||||||
|
acceptNewLines = true
|
||||||
|
}
|
||||||
|
|
||||||
|
str, err := l.lexStringAsString(terminator, discardLeadingNewLine, acceptNewLines)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return l.errorf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
l.emitWithValue(tokenString, str)
|
||||||
|
l.fastForward(len(terminator))
|
||||||
|
l.ignore()
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexTableKey() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
|
||||||
|
if l.peek() == '[' {
|
||||||
|
// token '[[' signifies an array of tables
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenDoubleLeftBracket)
|
||||||
|
return l.lexInsideTableArrayKey
|
||||||
|
}
|
||||||
|
// vanilla table key
|
||||||
|
l.emit(tokenLeftBracket)
|
||||||
|
return l.lexInsideTableKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexInsideTableArrayKey() tomlLexStateFn {
|
||||||
|
for r := l.peek(); r != eof; r = l.peek() {
|
||||||
|
switch r {
|
||||||
|
case ']':
|
||||||
|
if l.currentTokenStop > l.currentTokenStart {
|
||||||
|
l.emit(tokenKeyGroupArray)
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
if l.peek() != ']' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenDoubleRightBracket)
|
||||||
|
return l.lexVoid
|
||||||
|
case '[':
|
||||||
|
return l.errorf("table array key cannot contain ']'")
|
||||||
|
default:
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l.errorf("unclosed table array key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexInsideTableKey() tomlLexStateFn {
|
||||||
|
for r := l.peek(); r != eof; r = l.peek() {
|
||||||
|
switch r {
|
||||||
|
case ']':
|
||||||
|
if l.currentTokenStop > l.currentTokenStart {
|
||||||
|
l.emit(tokenKeyGroup)
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenRightBracket)
|
||||||
|
return l.lexVoid
|
||||||
|
case '[':
|
||||||
|
return l.errorf("table key cannot contain ']'")
|
||||||
|
default:
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l.errorf("unclosed table key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexRightBracket() tomlLexStateFn {
|
||||||
|
l.next()
|
||||||
|
l.emit(tokenRightBracket)
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) lexNumber() tomlLexStateFn {
|
||||||
|
r := l.peek()
|
||||||
|
if r == '+' || r == '-' {
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
pointSeen := false
|
||||||
|
expSeen := false
|
||||||
|
digitSeen := false
|
||||||
|
for {
|
||||||
|
next := l.peek()
|
||||||
|
if next == '.' {
|
||||||
|
if pointSeen {
|
||||||
|
return l.errorf("cannot have two dots in one float")
|
||||||
|
}
|
||||||
|
l.next()
|
||||||
|
if !isDigit(l.peek()) {
|
||||||
|
return l.errorf("float cannot end with a dot")
|
||||||
|
}
|
||||||
|
pointSeen = true
|
||||||
|
} else if next == 'e' || next == 'E' {
|
||||||
|
expSeen = true
|
||||||
|
l.next()
|
||||||
|
r := l.peek()
|
||||||
|
if r == '+' || r == '-' {
|
||||||
|
l.next()
|
||||||
|
}
|
||||||
|
} else if isDigit(next) {
|
||||||
|
digitSeen = true
|
||||||
|
l.next()
|
||||||
|
} else if next == '_' {
|
||||||
|
l.next()
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if pointSeen && !digitSeen {
|
||||||
|
return l.errorf("cannot start float with a dot")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !digitSeen {
|
||||||
|
return l.errorf("no digit in that number")
|
||||||
|
}
|
||||||
|
if pointSeen || expSeen {
|
||||||
|
l.emit(tokenFloat)
|
||||||
|
} else {
|
||||||
|
l.emit(tokenInteger)
|
||||||
|
}
|
||||||
|
return l.lexRvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *tomlLexer) run() {
|
||||||
|
for state := l.lexVoid; state != nil; {
|
||||||
|
state = state()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dateRegexp = regexp.MustCompile(`^\d{1,4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,9})?(Z|[+-]\d{2}:\d{2})`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry point
|
||||||
|
func lexToml(inputBytes []byte) []token {
|
||||||
|
runes := bytes.Runes(inputBytes)
|
||||||
|
l := &tomlLexer{
|
||||||
|
input: runes,
|
||||||
|
tokens: make([]token, 0, 256),
|
||||||
|
line: 1,
|
||||||
|
col: 1,
|
||||||
|
endbufferLine: 1,
|
||||||
|
endbufferCol: 1,
|
||||||
|
}
|
||||||
|
l.run()
|
||||||
|
return l.tokens
|
||||||
|
}
|
489
vendor/github.com/pelletier/go-toml/marshal.go
generated
vendored
Normal file
489
vendor/github.com/pelletier/go-toml/marshal.go
generated
vendored
Normal file
|
@ -0,0 +1,489 @@
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tomlOpts struct {
|
||||||
|
name string
|
||||||
|
include bool
|
||||||
|
omitempty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeType = reflect.TypeOf(time.Time{})
|
||||||
|
var marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
|
||||||
|
|
||||||
|
// Check if the given marshall type maps to a Tree primitive
|
||||||
|
func isPrimitive(mtype reflect.Type) bool {
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return isPrimitive(mtype.Elem())
|
||||||
|
case reflect.Bool:
|
||||||
|
return true
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return true
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return true
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return true
|
||||||
|
case reflect.String:
|
||||||
|
return true
|
||||||
|
case reflect.Struct:
|
||||||
|
return mtype == timeType || isCustomMarshaler(mtype)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the given marshall type maps to a Tree slice
|
||||||
|
func isTreeSlice(mtype reflect.Type) bool {
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Slice:
|
||||||
|
return !isOtherSlice(mtype)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the given marshall type maps to a non-Tree slice
|
||||||
|
func isOtherSlice(mtype reflect.Type) bool {
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
return isOtherSlice(mtype.Elem())
|
||||||
|
case reflect.Slice:
|
||||||
|
return isPrimitive(mtype.Elem()) || isOtherSlice(mtype.Elem())
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the given marshall type maps to a Tree
|
||||||
|
func isTree(mtype reflect.Type) bool {
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
return true
|
||||||
|
case reflect.Struct:
|
||||||
|
return !isPrimitive(mtype)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isCustomMarshaler(mtype reflect.Type) bool {
|
||||||
|
return mtype.Implements(marshalerType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func callCustomMarshaler(mval reflect.Value) ([]byte, error) {
|
||||||
|
return mval.Interface().(Marshaler).MarshalTOML()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshaler is the interface implemented by types that
|
||||||
|
// can marshal themselves into valid TOML.
|
||||||
|
type Marshaler interface {
|
||||||
|
MarshalTOML() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Marshal returns the TOML encoding of v. Behavior is similar to the Go json
|
||||||
|
encoder, except that there is no concept of a Marshaler interface or MarshalTOML
|
||||||
|
function for sub-structs, and currently only definite types can be marshaled
|
||||||
|
(i.e. no `interface{}`).
|
||||||
|
|
||||||
|
Note that pointers are automatically assigned the "omitempty" option, as TOML
|
||||||
|
explicity does not handle null values (saying instead the label should be
|
||||||
|
dropped).
|
||||||
|
|
||||||
|
Tree structural types and corresponding marshal types:
|
||||||
|
|
||||||
|
*Tree (*)struct, (*)map[string]interface{}
|
||||||
|
[]*Tree (*)[](*)struct, (*)[](*)map[string]interface{}
|
||||||
|
[]interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{})
|
||||||
|
interface{} (*)primitive
|
||||||
|
|
||||||
|
Tree primitive types and corresponding marshal types:
|
||||||
|
|
||||||
|
uint64 uint, uint8-uint64, pointers to same
|
||||||
|
int64 int, int8-uint64, pointers to same
|
||||||
|
float64 float32, float64, pointers to same
|
||||||
|
string string, pointers to same
|
||||||
|
bool bool, pointers to same
|
||||||
|
time.Time time.Time{}, pointers to same
|
||||||
|
*/
|
||||||
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
mtype := reflect.TypeOf(v)
|
||||||
|
if mtype.Kind() != reflect.Struct {
|
||||||
|
return []byte{}, errors.New("Only a struct can be marshaled to TOML")
|
||||||
|
}
|
||||||
|
sval := reflect.ValueOf(v)
|
||||||
|
if isCustomMarshaler(mtype) {
|
||||||
|
return callCustomMarshaler(sval)
|
||||||
|
}
|
||||||
|
t, err := valueToTree(mtype, sval)
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, err
|
||||||
|
}
|
||||||
|
s, err := t.ToTomlString()
|
||||||
|
return []byte(s), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert given marshal struct or map value to toml tree
|
||||||
|
func valueToTree(mtype reflect.Type, mval reflect.Value) (*Tree, error) {
|
||||||
|
if mtype.Kind() == reflect.Ptr {
|
||||||
|
return valueToTree(mtype.Elem(), mval.Elem())
|
||||||
|
}
|
||||||
|
tval := newTree()
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < mtype.NumField(); i++ {
|
||||||
|
mtypef, mvalf := mtype.Field(i), mval.Field(i)
|
||||||
|
opts := tomlOptions(mtypef)
|
||||||
|
if opts.include && (!opts.omitempty || !isZero(mvalf)) {
|
||||||
|
val, err := valueToToml(mtypef.Type, mvalf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tval.Set(opts.name, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
for _, key := range mval.MapKeys() {
|
||||||
|
mvalf := mval.MapIndex(key)
|
||||||
|
val, err := valueToToml(mtype.Elem(), mvalf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tval.Set(key.String(), val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert given marshal slice to slice of Toml trees
|
||||||
|
func valueToTreeSlice(mtype reflect.Type, mval reflect.Value) ([]*Tree, error) {
|
||||||
|
tval := make([]*Tree, mval.Len(), mval.Len())
|
||||||
|
for i := 0; i < mval.Len(); i++ {
|
||||||
|
val, err := valueToTree(mtype.Elem(), mval.Index(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tval[i] = val
|
||||||
|
}
|
||||||
|
return tval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert given marshal slice to slice of toml values
|
||||||
|
func valueToOtherSlice(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||||
|
tval := make([]interface{}, mval.Len(), mval.Len())
|
||||||
|
for i := 0; i < mval.Len(); i++ {
|
||||||
|
val, err := valueToToml(mtype.Elem(), mval.Index(i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tval[i] = val
|
||||||
|
}
|
||||||
|
return tval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert given marshal value to toml value
|
||||||
|
func valueToToml(mtype reflect.Type, mval reflect.Value) (interface{}, error) {
|
||||||
|
if mtype.Kind() == reflect.Ptr {
|
||||||
|
return valueToToml(mtype.Elem(), mval.Elem())
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case isCustomMarshaler(mtype):
|
||||||
|
return callCustomMarshaler(mval)
|
||||||
|
case isTree(mtype):
|
||||||
|
return valueToTree(mtype, mval)
|
||||||
|
case isTreeSlice(mtype):
|
||||||
|
return valueToTreeSlice(mtype, mval)
|
||||||
|
case isOtherSlice(mtype):
|
||||||
|
return valueToOtherSlice(mtype, mval)
|
||||||
|
default:
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
return mval.Bool(), nil
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return mval.Int(), nil
|
||||||
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
|
return mval.Uint(), nil
|
||||||
|
case reflect.Float32, reflect.Float64:
|
||||||
|
return mval.Float(), nil
|
||||||
|
case reflect.String:
|
||||||
|
return mval.String(), nil
|
||||||
|
case reflect.Struct:
|
||||||
|
return mval.Interface().(time.Time), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Marshal can't handle %v(%v)", mtype, mtype.Kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by v.
|
||||||
|
// Neither Unmarshaler interfaces nor UnmarshalTOML functions are supported for
|
||||||
|
// sub-structs, and only definite types can be unmarshaled.
|
||||||
|
func (t *Tree) Unmarshal(v interface{}) error {
|
||||||
|
mtype := reflect.TypeOf(v)
|
||||||
|
if mtype.Kind() != reflect.Ptr || mtype.Elem().Kind() != reflect.Struct {
|
||||||
|
return errors.New("Only a pointer to struct can be unmarshaled from TOML")
|
||||||
|
}
|
||||||
|
|
||||||
|
sval, err := valueFromTree(mtype.Elem(), t)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reflect.ValueOf(v).Elem().Set(sval)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the TOML-encoded data and stores the result in the value
|
||||||
|
// pointed to by v. Behavior is similar to the Go json encoder, except that there
|
||||||
|
// is no concept of an Unmarshaler interface or UnmarshalTOML function for
|
||||||
|
// sub-structs, and currently only definite types can be unmarshaled to (i.e. no
|
||||||
|
// `interface{}`).
|
||||||
|
//
|
||||||
|
// See Marshal() documentation for types mapping table.
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
t, err := LoadReader(bytes.NewReader(data))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.Unmarshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert toml tree to marshal struct or map, using marshal type
|
||||||
|
func valueFromTree(mtype reflect.Type, tval *Tree) (reflect.Value, error) {
|
||||||
|
if mtype.Kind() == reflect.Ptr {
|
||||||
|
return unwrapPointer(mtype, tval)
|
||||||
|
}
|
||||||
|
var mval reflect.Value
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Struct:
|
||||||
|
mval = reflect.New(mtype).Elem()
|
||||||
|
for i := 0; i < mtype.NumField(); i++ {
|
||||||
|
mtypef := mtype.Field(i)
|
||||||
|
opts := tomlOptions(mtypef)
|
||||||
|
if opts.include {
|
||||||
|
baseKey := opts.name
|
||||||
|
keysToTry := []string{baseKey, strings.ToLower(baseKey), strings.ToTitle(baseKey)}
|
||||||
|
for _, key := range keysToTry {
|
||||||
|
exists := tval.Has(key)
|
||||||
|
if !exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := tval.Get(key)
|
||||||
|
mvalf, err := valueFromToml(mtypef.Type, val)
|
||||||
|
if err != nil {
|
||||||
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
|
}
|
||||||
|
mval.Field(i).Set(mvalf)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
mval = reflect.MakeMap(mtype)
|
||||||
|
for _, key := range tval.Keys() {
|
||||||
|
val := tval.Get(key)
|
||||||
|
mvalf, err := valueFromToml(mtype.Elem(), val)
|
||||||
|
if err != nil {
|
||||||
|
return mval, formatError(err, tval.GetPosition(key))
|
||||||
|
}
|
||||||
|
mval.SetMapIndex(reflect.ValueOf(key), mvalf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert toml value to marshal struct/map slice, using marshal type
|
||||||
|
func valueFromTreeSlice(mtype reflect.Type, tval []*Tree) (reflect.Value, error) {
|
||||||
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
|
for i := 0; i < len(tval); i++ {
|
||||||
|
val, err := valueFromTree(mtype.Elem(), tval[i])
|
||||||
|
if err != nil {
|
||||||
|
return mval, err
|
||||||
|
}
|
||||||
|
mval.Index(i).Set(val)
|
||||||
|
}
|
||||||
|
return mval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert toml value to marshal primitive slice, using marshal type
|
||||||
|
func valueFromOtherSlice(mtype reflect.Type, tval []interface{}) (reflect.Value, error) {
|
||||||
|
mval := reflect.MakeSlice(mtype, len(tval), len(tval))
|
||||||
|
for i := 0; i < len(tval); i++ {
|
||||||
|
val, err := valueFromToml(mtype.Elem(), tval[i])
|
||||||
|
if err != nil {
|
||||||
|
return mval, err
|
||||||
|
}
|
||||||
|
mval.Index(i).Set(val)
|
||||||
|
}
|
||||||
|
return mval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert toml value to marshal value, using marshal type
|
||||||
|
func valueFromToml(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||||
|
if mtype.Kind() == reflect.Ptr {
|
||||||
|
return unwrapPointer(mtype, tval)
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case isTree(mtype):
|
||||||
|
return valueFromTree(mtype, tval.(*Tree))
|
||||||
|
case isTreeSlice(mtype):
|
||||||
|
return valueFromTreeSlice(mtype, tval.([]*Tree))
|
||||||
|
case isOtherSlice(mtype):
|
||||||
|
return valueFromOtherSlice(mtype, tval.([]interface{}))
|
||||||
|
default:
|
||||||
|
switch mtype.Kind() {
|
||||||
|
case reflect.Bool:
|
||||||
|
val, ok := tval.(bool)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to bool", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
case reflect.Int:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(int(val)), nil
|
||||||
|
case reflect.Int8:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(int8(val)), nil
|
||||||
|
case reflect.Int16:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(int16(val)), nil
|
||||||
|
case reflect.Int32:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(int32(val)), nil
|
||||||
|
case reflect.Int64:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to int", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
case reflect.Uint:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(uint(val)), nil
|
||||||
|
case reflect.Uint8:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(uint8(val)), nil
|
||||||
|
case reflect.Uint16:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(uint16(val)), nil
|
||||||
|
case reflect.Uint32:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(uint32(val)), nil
|
||||||
|
case reflect.Uint64:
|
||||||
|
val, ok := tval.(int64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to uint", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(uint64(val)), nil
|
||||||
|
case reflect.Float32:
|
||||||
|
val, ok := tval.(float64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to float", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(float32(val)), nil
|
||||||
|
case reflect.Float64:
|
||||||
|
val, ok := tval.(float64)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to float", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
case reflect.String:
|
||||||
|
val, ok := tval.(string)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to string", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
case reflect.Struct:
|
||||||
|
val, ok := tval.(time.Time)
|
||||||
|
if !ok {
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Can't convert %v(%T) to time", tval, tval)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(val), nil
|
||||||
|
default:
|
||||||
|
return reflect.ValueOf(nil), fmt.Errorf("Unmarshal can't handle %v(%v)", mtype, mtype.Kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unwrapPointer(mtype reflect.Type, tval interface{}) (reflect.Value, error) {
|
||||||
|
val, err := valueFromToml(mtype.Elem(), tval)
|
||||||
|
if err != nil {
|
||||||
|
return reflect.ValueOf(nil), err
|
||||||
|
}
|
||||||
|
mval := reflect.New(mtype.Elem())
|
||||||
|
mval.Elem().Set(val)
|
||||||
|
return mval, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tomlOptions(vf reflect.StructField) tomlOpts {
|
||||||
|
tag := vf.Tag.Get("toml")
|
||||||
|
parse := strings.Split(tag, ",")
|
||||||
|
result := tomlOpts{vf.Name, true, false}
|
||||||
|
if parse[0] != "" {
|
||||||
|
if parse[0] == "-" && len(parse) == 1 {
|
||||||
|
result.include = false
|
||||||
|
} else {
|
||||||
|
result.name = strings.Trim(parse[0], " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vf.PkgPath != "" {
|
||||||
|
result.include = false
|
||||||
|
}
|
||||||
|
if len(parse) > 1 && strings.Trim(parse[1], " ") == "omitempty" {
|
||||||
|
result.omitempty = true
|
||||||
|
}
|
||||||
|
if vf.Type.Kind() == reflect.Ptr {
|
||||||
|
result.omitempty = true
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func isZero(val reflect.Value) bool {
|
||||||
|
switch val.Type().Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Array:
|
||||||
|
fallthrough
|
||||||
|
case reflect.Slice:
|
||||||
|
return val.Len() == 0
|
||||||
|
default:
|
||||||
|
return reflect.DeepEqual(val.Interface(), reflect.Zero(val.Type()).Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatError(err error, pos Position) error {
|
||||||
|
if err.Error()[0] == '(' { // Error already contains position information
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: %s", pos, err)
|
||||||
|
}
|
38
vendor/github.com/pelletier/go-toml/marshal_test.toml
generated
vendored
Normal file
38
vendor/github.com/pelletier/go-toml/marshal_test.toml
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
title = "TOML Marshal Testing"
|
||||||
|
|
||||||
|
[basic]
|
||||||
|
bool = true
|
||||||
|
date = 1979-05-27T07:32:00Z
|
||||||
|
float = 123.4
|
||||||
|
int = 5000
|
||||||
|
string = "Bite me"
|
||||||
|
uint = 5001
|
||||||
|
|
||||||
|
[basic_lists]
|
||||||
|
bools = [true,false,true]
|
||||||
|
dates = [1979-05-27T07:32:00Z,1980-05-27T07:32:00Z]
|
||||||
|
floats = [12.3,45.6,78.9]
|
||||||
|
ints = [8001,8001,8002]
|
||||||
|
strings = ["One","Two","Three"]
|
||||||
|
uints = [5002,5003]
|
||||||
|
|
||||||
|
[basic_map]
|
||||||
|
one = "one"
|
||||||
|
two = "two"
|
||||||
|
|
||||||
|
[subdoc]
|
||||||
|
|
||||||
|
[subdoc.first]
|
||||||
|
name = "First"
|
||||||
|
|
||||||
|
[subdoc.second]
|
||||||
|
name = "Second"
|
||||||
|
|
||||||
|
[[subdoclist]]
|
||||||
|
name = "List.First"
|
||||||
|
|
||||||
|
[[subdoclist]]
|
||||||
|
name = "List.Second"
|
||||||
|
|
||||||
|
[[subdocptrs]]
|
||||||
|
name = "Second"
|
383
vendor/github.com/pelletier/go-toml/parser.go
generated
vendored
Normal file
383
vendor/github.com/pelletier/go-toml/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
// TOML Parser.
|
||||||
|
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tomlParser struct {
|
||||||
|
flowIdx int
|
||||||
|
flow []token
|
||||||
|
tree *Tree
|
||||||
|
currentTable []string
|
||||||
|
seenTableKeys []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type tomlParserStateFn func() tomlParserStateFn
|
||||||
|
|
||||||
|
// Formats and panics an error message based on a token
|
||||||
|
func (p *tomlParser) raiseError(tok *token, msg string, args ...interface{}) {
|
||||||
|
panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) run() {
|
||||||
|
for state := p.parseStart; state != nil; {
|
||||||
|
state = state()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) peek() *token {
|
||||||
|
if p.flowIdx >= len(p.flow) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &p.flow[p.flowIdx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) assume(typ tokenType) {
|
||||||
|
tok := p.getToken()
|
||||||
|
if tok == nil {
|
||||||
|
p.raiseError(tok, "was expecting token %s, but token stream is empty", tok)
|
||||||
|
}
|
||||||
|
if tok.typ != typ {
|
||||||
|
p.raiseError(tok, "was expecting token %s, but got %s instead", typ, tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) getToken() *token {
|
||||||
|
tok := p.peek()
|
||||||
|
if tok == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.flowIdx++
|
||||||
|
return tok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseStart() tomlParserStateFn {
|
||||||
|
tok := p.peek()
|
||||||
|
|
||||||
|
// end of stream, parsing is finished
|
||||||
|
if tok == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tok.typ {
|
||||||
|
case tokenDoubleLeftBracket:
|
||||||
|
return p.parseGroupArray
|
||||||
|
case tokenLeftBracket:
|
||||||
|
return p.parseGroup
|
||||||
|
case tokenKey:
|
||||||
|
return p.parseAssign
|
||||||
|
case tokenEOF:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
p.raiseError(tok, "unexpected token")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseGroupArray() tomlParserStateFn {
|
||||||
|
startToken := p.getToken() // discard the [[
|
||||||
|
key := p.getToken()
|
||||||
|
if key.typ != tokenKeyGroupArray {
|
||||||
|
p.raiseError(key, "unexpected token %s, was expecting a table array key", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get or create table array element at the indicated part in the path
|
||||||
|
keys, err := parseKey(key.val)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(key, "invalid table array key: %s", err)
|
||||||
|
}
|
||||||
|
p.tree.createSubTree(keys[:len(keys)-1], startToken.Position) // create parent entries
|
||||||
|
destTree := p.tree.GetPath(keys)
|
||||||
|
var array []*Tree
|
||||||
|
if destTree == nil {
|
||||||
|
array = make([]*Tree, 0)
|
||||||
|
} else if target, ok := destTree.([]*Tree); ok && target != nil {
|
||||||
|
array = destTree.([]*Tree)
|
||||||
|
} else {
|
||||||
|
p.raiseError(key, "key %s is already assigned and not of type table array", key)
|
||||||
|
}
|
||||||
|
p.currentTable = keys
|
||||||
|
|
||||||
|
// add a new tree to the end of the table array
|
||||||
|
newTree := newTree()
|
||||||
|
newTree.position = startToken.Position
|
||||||
|
array = append(array, newTree)
|
||||||
|
p.tree.SetPath(p.currentTable, array)
|
||||||
|
|
||||||
|
// remove all keys that were children of this table array
|
||||||
|
prefix := key.val + "."
|
||||||
|
found := false
|
||||||
|
for ii := 0; ii < len(p.seenTableKeys); {
|
||||||
|
tableKey := p.seenTableKeys[ii]
|
||||||
|
if strings.HasPrefix(tableKey, prefix) {
|
||||||
|
p.seenTableKeys = append(p.seenTableKeys[:ii], p.seenTableKeys[ii+1:]...)
|
||||||
|
} else {
|
||||||
|
found = (tableKey == key.val)
|
||||||
|
ii++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep this key name from use by other kinds of assignments
|
||||||
|
if !found {
|
||||||
|
p.seenTableKeys = append(p.seenTableKeys, key.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// move to next parser state
|
||||||
|
p.assume(tokenDoubleRightBracket)
|
||||||
|
return p.parseStart
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseGroup() tomlParserStateFn {
|
||||||
|
startToken := p.getToken() // discard the [
|
||||||
|
key := p.getToken()
|
||||||
|
if key.typ != tokenKeyGroup {
|
||||||
|
p.raiseError(key, "unexpected token %s, was expecting a table key", key)
|
||||||
|
}
|
||||||
|
for _, item := range p.seenTableKeys {
|
||||||
|
if item == key.val {
|
||||||
|
p.raiseError(key, "duplicated tables")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.seenTableKeys = append(p.seenTableKeys, key.val)
|
||||||
|
keys, err := parseKey(key.val)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(key, "invalid table array key: %s", err)
|
||||||
|
}
|
||||||
|
if err := p.tree.createSubTree(keys, startToken.Position); err != nil {
|
||||||
|
p.raiseError(key, "%s", err)
|
||||||
|
}
|
||||||
|
p.assume(tokenRightBracket)
|
||||||
|
p.currentTable = keys
|
||||||
|
return p.parseStart
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseAssign() tomlParserStateFn {
|
||||||
|
key := p.getToken()
|
||||||
|
p.assume(tokenEqual)
|
||||||
|
|
||||||
|
value := p.parseRvalue()
|
||||||
|
var tableKey []string
|
||||||
|
if len(p.currentTable) > 0 {
|
||||||
|
tableKey = p.currentTable
|
||||||
|
} else {
|
||||||
|
tableKey = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the table to assign, looking out for arrays of tables
|
||||||
|
var targetNode *Tree
|
||||||
|
switch node := p.tree.GetPath(tableKey).(type) {
|
||||||
|
case []*Tree:
|
||||||
|
targetNode = node[len(node)-1]
|
||||||
|
case *Tree:
|
||||||
|
targetNode = node
|
||||||
|
default:
|
||||||
|
p.raiseError(key, "Unknown table type for path: %s",
|
||||||
|
strings.Join(tableKey, "."))
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign value to the found table
|
||||||
|
keyVals, err := parseKey(key.val)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(key, "%s", err)
|
||||||
|
}
|
||||||
|
if len(keyVals) != 1 {
|
||||||
|
p.raiseError(key, "Invalid key")
|
||||||
|
}
|
||||||
|
keyVal := keyVals[0]
|
||||||
|
localKey := []string{keyVal}
|
||||||
|
finalKey := append(tableKey, keyVal)
|
||||||
|
if targetNode.GetPath(localKey) != nil {
|
||||||
|
p.raiseError(key, "The following key was defined twice: %s",
|
||||||
|
strings.Join(finalKey, "."))
|
||||||
|
}
|
||||||
|
var toInsert interface{}
|
||||||
|
|
||||||
|
switch value.(type) {
|
||||||
|
case *Tree, []*Tree:
|
||||||
|
toInsert = value
|
||||||
|
default:
|
||||||
|
toInsert = &tomlValue{value, key.Position}
|
||||||
|
}
|
||||||
|
targetNode.values[keyVal] = toInsert
|
||||||
|
return p.parseStart
|
||||||
|
}
|
||||||
|
|
||||||
|
var numberUnderscoreInvalidRegexp *regexp.Regexp
|
||||||
|
|
||||||
|
func cleanupNumberToken(value string) (string, error) {
|
||||||
|
if numberUnderscoreInvalidRegexp.MatchString(value) {
|
||||||
|
return "", errors.New("invalid use of _ in number")
|
||||||
|
}
|
||||||
|
cleanedVal := strings.Replace(value, "_", "", -1)
|
||||||
|
return cleanedVal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseRvalue() interface{} {
|
||||||
|
tok := p.getToken()
|
||||||
|
if tok == nil || tok.typ == tokenEOF {
|
||||||
|
p.raiseError(tok, "expecting a value")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tok.typ {
|
||||||
|
case tokenString:
|
||||||
|
return tok.val
|
||||||
|
case tokenTrue:
|
||||||
|
return true
|
||||||
|
case tokenFalse:
|
||||||
|
return false
|
||||||
|
case tokenInteger:
|
||||||
|
cleanedVal, err := cleanupNumberToken(tok.val)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(tok, "%s", err)
|
||||||
|
}
|
||||||
|
val, err := strconv.ParseInt(cleanedVal, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(tok, "%s", err)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
case tokenFloat:
|
||||||
|
cleanedVal, err := cleanupNumberToken(tok.val)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(tok, "%s", err)
|
||||||
|
}
|
||||||
|
val, err := strconv.ParseFloat(cleanedVal, 64)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(tok, "%s", err)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
case tokenDate:
|
||||||
|
val, err := time.ParseInLocation(time.RFC3339Nano, tok.val, time.UTC)
|
||||||
|
if err != nil {
|
||||||
|
p.raiseError(tok, "%s", err)
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
case tokenLeftBracket:
|
||||||
|
return p.parseArray()
|
||||||
|
case tokenLeftCurlyBrace:
|
||||||
|
return p.parseInlineTable()
|
||||||
|
case tokenEqual:
|
||||||
|
p.raiseError(tok, "cannot have multiple equals for the same key")
|
||||||
|
case tokenError:
|
||||||
|
p.raiseError(tok, "%s", tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.raiseError(tok, "never reached")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tokenIsComma(t *token) bool {
|
||||||
|
return t != nil && t.typ == tokenComma
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseInlineTable() *Tree {
|
||||||
|
tree := newTree()
|
||||||
|
var previous *token
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
follow := p.peek()
|
||||||
|
if follow == nil || follow.typ == tokenEOF {
|
||||||
|
p.raiseError(follow, "unterminated inline table")
|
||||||
|
}
|
||||||
|
switch follow.typ {
|
||||||
|
case tokenRightCurlyBrace:
|
||||||
|
p.getToken()
|
||||||
|
break Loop
|
||||||
|
case tokenKey:
|
||||||
|
if !tokenIsComma(previous) && previous != nil {
|
||||||
|
p.raiseError(follow, "comma expected between fields in inline table")
|
||||||
|
}
|
||||||
|
key := p.getToken()
|
||||||
|
p.assume(tokenEqual)
|
||||||
|
value := p.parseRvalue()
|
||||||
|
tree.Set(key.val, value)
|
||||||
|
case tokenComma:
|
||||||
|
if previous == nil {
|
||||||
|
p.raiseError(follow, "inline table cannot start with a comma")
|
||||||
|
}
|
||||||
|
if tokenIsComma(previous) {
|
||||||
|
p.raiseError(follow, "need field between two commas in inline table")
|
||||||
|
}
|
||||||
|
p.getToken()
|
||||||
|
default:
|
||||||
|
p.raiseError(follow, "unexpected token type in inline table: %s", follow.typ.String())
|
||||||
|
}
|
||||||
|
previous = follow
|
||||||
|
}
|
||||||
|
if tokenIsComma(previous) {
|
||||||
|
p.raiseError(previous, "trailing comma at the end of inline table")
|
||||||
|
}
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tomlParser) parseArray() interface{} {
|
||||||
|
var array []interface{}
|
||||||
|
arrayType := reflect.TypeOf(nil)
|
||||||
|
for {
|
||||||
|
follow := p.peek()
|
||||||
|
if follow == nil || follow.typ == tokenEOF {
|
||||||
|
p.raiseError(follow, "unterminated array")
|
||||||
|
}
|
||||||
|
if follow.typ == tokenRightBracket {
|
||||||
|
p.getToken()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
val := p.parseRvalue()
|
||||||
|
if arrayType == nil {
|
||||||
|
arrayType = reflect.TypeOf(val)
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(val) != arrayType {
|
||||||
|
p.raiseError(follow, "mixed types in array")
|
||||||
|
}
|
||||||
|
array = append(array, val)
|
||||||
|
follow = p.peek()
|
||||||
|
if follow == nil || follow.typ == tokenEOF {
|
||||||
|
p.raiseError(follow, "unterminated array")
|
||||||
|
}
|
||||||
|
if follow.typ != tokenRightBracket && follow.typ != tokenComma {
|
||||||
|
p.raiseError(follow, "missing comma")
|
||||||
|
}
|
||||||
|
if follow.typ == tokenComma {
|
||||||
|
p.getToken()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// An array of Trees is actually an array of inline
|
||||||
|
// tables, which is a shorthand for a table array. If the
|
||||||
|
// array was not converted from []interface{} to []*Tree,
|
||||||
|
// the two notations would not be equivalent.
|
||||||
|
if arrayType == reflect.TypeOf(newTree()) {
|
||||||
|
tomlArray := make([]*Tree, len(array))
|
||||||
|
for i, v := range array {
|
||||||
|
tomlArray[i] = v.(*Tree)
|
||||||
|
}
|
||||||
|
return tomlArray
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseToml(flow []token) *Tree {
|
||||||
|
result := newTree()
|
||||||
|
result.position = Position{1, 1}
|
||||||
|
parser := &tomlParser{
|
||||||
|
flowIdx: 0,
|
||||||
|
flow: flow,
|
||||||
|
tree: result,
|
||||||
|
currentTable: make([]string, 0),
|
||||||
|
seenTableKeys: make([]string, 0),
|
||||||
|
}
|
||||||
|
parser.run()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
numberUnderscoreInvalidRegexp = regexp.MustCompile(`([^\d]_|_[^\d]|_$|^_)`)
|
||||||
|
}
|
29
vendor/github.com/pelletier/go-toml/position.go
generated
vendored
Normal file
29
vendor/github.com/pelletier/go-toml/position.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Position support for go-toml
|
||||||
|
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Position of a document element within a TOML document.
|
||||||
|
//
|
||||||
|
// Line and Col are both 1-indexed positions for the element's line number and
|
||||||
|
// column number, respectively. Values of zero or less will cause Invalid(),
|
||||||
|
// to return true.
|
||||||
|
type Position struct {
|
||||||
|
Line int // line within the document
|
||||||
|
Col int // column within the line
|
||||||
|
}
|
||||||
|
|
||||||
|
// String representation of the position.
|
||||||
|
// Displays 1-indexed line and column numbers.
|
||||||
|
func (p Position) String() string {
|
||||||
|
return fmt.Sprintf("(%d, %d)", p.Line, p.Col)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid returns whether or not the position is valid (i.e. with negative or
|
||||||
|
// null values)
|
||||||
|
func (p Position) Invalid() bool {
|
||||||
|
return p.Line <= 0 || p.Col <= 0
|
||||||
|
}
|
90
vendor/github.com/pelletier/go-toml/test.sh
generated
vendored
Executable file
90
vendor/github.com/pelletier/go-toml/test.sh
generated
vendored
Executable file
|
@ -0,0 +1,90 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# fail out of the script if anything here fails
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# set the path to the present working directory
|
||||||
|
export GOPATH=`pwd`
|
||||||
|
|
||||||
|
function git_clone() {
|
||||||
|
path=$1
|
||||||
|
branch=$2
|
||||||
|
version=$3
|
||||||
|
if [ ! -d "src/$path" ]; then
|
||||||
|
mkdir -p src/$path
|
||||||
|
git clone https://$path.git src/$path
|
||||||
|
fi
|
||||||
|
pushd src/$path
|
||||||
|
git checkout "$branch"
|
||||||
|
git reset --hard "$version"
|
||||||
|
popd
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove potential previous runs
|
||||||
|
rm -rf src test_program_bin toml-test
|
||||||
|
|
||||||
|
# Run go vet
|
||||||
|
go vet ./...
|
||||||
|
|
||||||
|
go get github.com/pelletier/go-buffruneio
|
||||||
|
go get github.com/davecgh/go-spew/spew
|
||||||
|
go get gopkg.in/yaml.v2
|
||||||
|
go get github.com/BurntSushi/toml
|
||||||
|
|
||||||
|
# get code for BurntSushi TOML validation
|
||||||
|
# pinning all to 'HEAD' for version 0.3.x work (TODO: pin to commit hash when tests stabilize)
|
||||||
|
git_clone github.com/BurntSushi/toml master HEAD
|
||||||
|
git_clone github.com/BurntSushi/toml-test master HEAD #was: 0.2.0 HEAD
|
||||||
|
|
||||||
|
# build the BurntSushi test application
|
||||||
|
go build -o toml-test github.com/BurntSushi/toml-test
|
||||||
|
|
||||||
|
# vendorize the current lib for testing
|
||||||
|
# NOTE: this basically mocks an install without having to go back out to github for code
|
||||||
|
mkdir -p src/github.com/pelletier/go-toml/cmd
|
||||||
|
mkdir -p src/github.com/pelletier/go-toml/query
|
||||||
|
cp *.go *.toml src/github.com/pelletier/go-toml
|
||||||
|
cp -R cmd/* src/github.com/pelletier/go-toml/cmd
|
||||||
|
cp -R query/* src/github.com/pelletier/go-toml/query
|
||||||
|
go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go
|
||||||
|
|
||||||
|
# Run basic unit tests
|
||||||
|
go test github.com/pelletier/go-toml -covermode=count -coverprofile=coverage.out
|
||||||
|
go test github.com/pelletier/go-toml/cmd/tomljson
|
||||||
|
go test github.com/pelletier/go-toml/query
|
||||||
|
|
||||||
|
# run the entire BurntSushi test suite
|
||||||
|
if [[ $# -eq 0 ]] ; then
|
||||||
|
echo "Running all BurntSushi tests"
|
||||||
|
./toml-test ./test_program_bin | tee test_out
|
||||||
|
else
|
||||||
|
# run a specific test
|
||||||
|
test=$1
|
||||||
|
test_path='src/github.com/BurntSushi/toml-test/tests'
|
||||||
|
valid_test="$test_path/valid/$test"
|
||||||
|
invalid_test="$test_path/invalid/$test"
|
||||||
|
|
||||||
|
if [ -e "$valid_test.toml" ]; then
|
||||||
|
echo "Valid Test TOML for $test:"
|
||||||
|
echo "===="
|
||||||
|
cat "$valid_test.toml"
|
||||||
|
|
||||||
|
echo "Valid Test JSON for $test:"
|
||||||
|
echo "===="
|
||||||
|
cat "$valid_test.json"
|
||||||
|
|
||||||
|
echo "Go-TOML Output for $test:"
|
||||||
|
echo "===="
|
||||||
|
cat "$valid_test.toml" | ./test_program_bin
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e "$invalid_test.toml" ]; then
|
||||||
|
echo "Invalid Test TOML for $test:"
|
||||||
|
echo "===="
|
||||||
|
cat "$invalid_test.toml"
|
||||||
|
|
||||||
|
echo "Go-TOML Output for $test:"
|
||||||
|
echo "===="
|
||||||
|
echo "go-toml Output:"
|
||||||
|
cat "$invalid_test.toml" | ./test_program_bin
|
||||||
|
fi
|
||||||
|
fi
|
140
vendor/github.com/pelletier/go-toml/token.go
generated
vendored
Normal file
140
vendor/github.com/pelletier/go-toml/token.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define tokens
|
||||||
|
type tokenType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
eof = -(iota + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tokenError tokenType = iota
|
||||||
|
tokenEOF
|
||||||
|
tokenComment
|
||||||
|
tokenKey
|
||||||
|
tokenString
|
||||||
|
tokenInteger
|
||||||
|
tokenTrue
|
||||||
|
tokenFalse
|
||||||
|
tokenFloat
|
||||||
|
tokenEqual
|
||||||
|
tokenLeftBracket
|
||||||
|
tokenRightBracket
|
||||||
|
tokenLeftCurlyBrace
|
||||||
|
tokenRightCurlyBrace
|
||||||
|
tokenLeftParen
|
||||||
|
tokenRightParen
|
||||||
|
tokenDoubleLeftBracket
|
||||||
|
tokenDoubleRightBracket
|
||||||
|
tokenDate
|
||||||
|
tokenKeyGroup
|
||||||
|
tokenKeyGroupArray
|
||||||
|
tokenComma
|
||||||
|
tokenColon
|
||||||
|
tokenDollar
|
||||||
|
tokenStar
|
||||||
|
tokenQuestion
|
||||||
|
tokenDot
|
||||||
|
tokenDotDot
|
||||||
|
tokenEOL
|
||||||
|
)
|
||||||
|
|
||||||
|
var tokenTypeNames = []string{
|
||||||
|
"Error",
|
||||||
|
"EOF",
|
||||||
|
"Comment",
|
||||||
|
"Key",
|
||||||
|
"String",
|
||||||
|
"Integer",
|
||||||
|
"True",
|
||||||
|
"False",
|
||||||
|
"Float",
|
||||||
|
"=",
|
||||||
|
"[",
|
||||||
|
"]",
|
||||||
|
"{",
|
||||||
|
"}",
|
||||||
|
"(",
|
||||||
|
")",
|
||||||
|
"]]",
|
||||||
|
"[[",
|
||||||
|
"Date",
|
||||||
|
"KeyGroup",
|
||||||
|
"KeyGroupArray",
|
||||||
|
",",
|
||||||
|
":",
|
||||||
|
"$",
|
||||||
|
"*",
|
||||||
|
"?",
|
||||||
|
".",
|
||||||
|
"..",
|
||||||
|
"EOL",
|
||||||
|
}
|
||||||
|
|
||||||
|
type token struct {
|
||||||
|
Position
|
||||||
|
typ tokenType
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt tokenType) String() string {
|
||||||
|
idx := int(tt)
|
||||||
|
if idx < len(tokenTypeNames) {
|
||||||
|
return tokenTypeNames[idx]
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t token) Int() int {
|
||||||
|
if result, err := strconv.Atoi(t.val); err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t token) String() string {
|
||||||
|
switch t.typ {
|
||||||
|
case tokenEOF:
|
||||||
|
return "EOF"
|
||||||
|
case tokenError:
|
||||||
|
return t.val
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%q", t.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(r rune) bool {
|
||||||
|
return r == ' ' || r == '\t'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlphanumeric(r rune) bool {
|
||||||
|
return unicode.IsLetter(r) || r == '_'
|
||||||
|
}
|
||||||
|
|
||||||
|
func isKeyChar(r rune) bool {
|
||||||
|
// Keys start with the first character that isn't whitespace or [ and end
|
||||||
|
// with the last non-whitespace character before the equals sign. Keys
|
||||||
|
// cannot contain a # character."
|
||||||
|
return !(r == '\r' || r == '\n' || r == eof || r == '=')
|
||||||
|
}
|
||||||
|
|
||||||
|
func isKeyStartChar(r rune) bool {
|
||||||
|
return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '[')
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDigit(r rune) bool {
|
||||||
|
return unicode.IsNumber(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isHexDigit(r rune) bool {
|
||||||
|
return isDigit(r) ||
|
||||||
|
(r >= 'a' && r <= 'f') ||
|
||||||
|
(r >= 'A' && r <= 'F')
|
||||||
|
}
|
292
vendor/github.com/pelletier/go-toml/toml.go
generated
vendored
Normal file
292
vendor/github.com/pelletier/go-toml/toml.go
generated
vendored
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tomlValue struct {
|
||||||
|
value interface{} // string, int64, uint64, float64, bool, time.Time, [] of any of this list
|
||||||
|
position Position
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tree is the result of the parsing of a TOML file.
|
||||||
|
type Tree struct {
|
||||||
|
values map[string]interface{} // string -> *tomlValue, *Tree, []*Tree
|
||||||
|
position Position
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTree() *Tree {
|
||||||
|
return &Tree{
|
||||||
|
values: make(map[string]interface{}),
|
||||||
|
position: Position{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TreeFromMap initializes a new Tree object using the given map.
|
||||||
|
func TreeFromMap(m map[string]interface{}) (*Tree, error) {
|
||||||
|
result, err := toTree(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result.(*Tree), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position returns the position of the tree.
|
||||||
|
func (t *Tree) Position() Position {
|
||||||
|
return t.position
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has returns a boolean indicating if the given key exists.
|
||||||
|
func (t *Tree) Has(key string) bool {
|
||||||
|
if key == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return t.HasPath(strings.Split(key, "."))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPath returns true if the given path of keys exists, false otherwise.
|
||||||
|
func (t *Tree) HasPath(keys []string) bool {
|
||||||
|
return t.GetPath(keys) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keys returns the keys of the toplevel tree (does not recurse).
|
||||||
|
func (t *Tree) Keys() []string {
|
||||||
|
keys := make([]string, len(t.values))
|
||||||
|
i := 0
|
||||||
|
for k := range t.values {
|
||||||
|
keys[i] = k
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the value at key in the Tree.
|
||||||
|
// Key is a dot-separated path (e.g. a.b.c).
|
||||||
|
// Returns nil if the path does not exist in the tree.
|
||||||
|
// If keys is of length zero, the current tree is returned.
|
||||||
|
func (t *Tree) Get(key string) interface{} {
|
||||||
|
if key == "" {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
comps, err := parseKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.GetPath(comps)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPath returns the element in the tree indicated by 'keys'.
|
||||||
|
// If keys is of length zero, the current tree is returned.
|
||||||
|
func (t *Tree) GetPath(keys []string) interface{} {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
subtree := t
|
||||||
|
for _, intermediateKey := range keys[:len(keys)-1] {
|
||||||
|
value, exists := subtree.values[intermediateKey]
|
||||||
|
if !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch node := value.(type) {
|
||||||
|
case *Tree:
|
||||||
|
subtree = node
|
||||||
|
case []*Tree:
|
||||||
|
// go to most recent element
|
||||||
|
if len(node) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
subtree = node[len(node)-1]
|
||||||
|
default:
|
||||||
|
return nil // cannot navigate through other node types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// branch based on final node type
|
||||||
|
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
||||||
|
case *tomlValue:
|
||||||
|
return node.value
|
||||||
|
default:
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPosition returns the position of the given key.
|
||||||
|
func (t *Tree) GetPosition(key string) Position {
|
||||||
|
if key == "" {
|
||||||
|
return t.position
|
||||||
|
}
|
||||||
|
return t.GetPositionPath(strings.Split(key, "."))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPositionPath returns the element in the tree indicated by 'keys'.
|
||||||
|
// If keys is of length zero, the current tree is returned.
|
||||||
|
func (t *Tree) GetPositionPath(keys []string) Position {
|
||||||
|
if len(keys) == 0 {
|
||||||
|
return t.position
|
||||||
|
}
|
||||||
|
subtree := t
|
||||||
|
for _, intermediateKey := range keys[:len(keys)-1] {
|
||||||
|
value, exists := subtree.values[intermediateKey]
|
||||||
|
if !exists {
|
||||||
|
return Position{0, 0}
|
||||||
|
}
|
||||||
|
switch node := value.(type) {
|
||||||
|
case *Tree:
|
||||||
|
subtree = node
|
||||||
|
case []*Tree:
|
||||||
|
// go to most recent element
|
||||||
|
if len(node) == 0 {
|
||||||
|
return Position{0, 0}
|
||||||
|
}
|
||||||
|
subtree = node[len(node)-1]
|
||||||
|
default:
|
||||||
|
return Position{0, 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// branch based on final node type
|
||||||
|
switch node := subtree.values[keys[len(keys)-1]].(type) {
|
||||||
|
case *tomlValue:
|
||||||
|
return node.position
|
||||||
|
case *Tree:
|
||||||
|
return node.position
|
||||||
|
case []*Tree:
|
||||||
|
// go to most recent element
|
||||||
|
if len(node) == 0 {
|
||||||
|
return Position{0, 0}
|
||||||
|
}
|
||||||
|
return node[len(node)-1].position
|
||||||
|
default:
|
||||||
|
return Position{0, 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDefault works like Get but with a default value
|
||||||
|
func (t *Tree) GetDefault(key string, def interface{}) interface{} {
|
||||||
|
val := t.Get(key)
|
||||||
|
if val == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set an element in the tree.
|
||||||
|
// Key is a dot-separated path (e.g. a.b.c).
|
||||||
|
// Creates all necessary intermediate trees, if needed.
|
||||||
|
func (t *Tree) Set(key string, value interface{}) {
|
||||||
|
t.SetPath(strings.Split(key, "."), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPath sets an element in the tree.
|
||||||
|
// Keys is an array of path elements (e.g. {"a","b","c"}).
|
||||||
|
// Creates all necessary intermediate trees, if needed.
|
||||||
|
func (t *Tree) SetPath(keys []string, value interface{}) {
|
||||||
|
subtree := t
|
||||||
|
for _, intermediateKey := range keys[:len(keys)-1] {
|
||||||
|
nextTree, exists := subtree.values[intermediateKey]
|
||||||
|
if !exists {
|
||||||
|
nextTree = newTree()
|
||||||
|
subtree.values[intermediateKey] = nextTree // add new element here
|
||||||
|
}
|
||||||
|
switch node := nextTree.(type) {
|
||||||
|
case *Tree:
|
||||||
|
subtree = node
|
||||||
|
case []*Tree:
|
||||||
|
// go to most recent element
|
||||||
|
if len(node) == 0 {
|
||||||
|
// create element if it does not exist
|
||||||
|
subtree.values[intermediateKey] = append(node, newTree())
|
||||||
|
}
|
||||||
|
subtree = node[len(node)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var toInsert interface{}
|
||||||
|
|
||||||
|
switch value.(type) {
|
||||||
|
case *Tree:
|
||||||
|
toInsert = value
|
||||||
|
case []*Tree:
|
||||||
|
toInsert = value
|
||||||
|
case *tomlValue:
|
||||||
|
toInsert = value
|
||||||
|
default:
|
||||||
|
toInsert = &tomlValue{value: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
subtree.values[keys[len(keys)-1]] = toInsert
|
||||||
|
}
|
||||||
|
|
||||||
|
// createSubTree takes a tree and a key and create the necessary intermediate
|
||||||
|
// subtrees to create a subtree at that point. In-place.
|
||||||
|
//
|
||||||
|
// e.g. passing a.b.c will create (assuming tree is empty) tree[a], tree[a][b]
|
||||||
|
// and tree[a][b][c]
|
||||||
|
//
|
||||||
|
// Returns nil on success, error object on failure
|
||||||
|
func (t *Tree) createSubTree(keys []string, pos Position) error {
|
||||||
|
subtree := t
|
||||||
|
for _, intermediateKey := range keys {
|
||||||
|
nextTree, exists := subtree.values[intermediateKey]
|
||||||
|
if !exists {
|
||||||
|
tree := newTree()
|
||||||
|
tree.position = pos
|
||||||
|
subtree.values[intermediateKey] = tree
|
||||||
|
nextTree = tree
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := nextTree.(type) {
|
||||||
|
case []*Tree:
|
||||||
|
subtree = node[len(node)-1]
|
||||||
|
case *Tree:
|
||||||
|
subtree = node
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown type for path %s (%s): %T (%#v)",
|
||||||
|
strings.Join(keys, "."), intermediateKey, nextTree, nextTree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadBytes creates a Tree from a []byte.
|
||||||
|
func LoadBytes(b []byte) (tree *Tree, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if _, ok := r.(runtime.Error); ok {
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
err = errors.New(r.(string))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
tree = parseToml(lexToml(b))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadReader creates a Tree from any io.Reader.
|
||||||
|
func LoadReader(reader io.Reader) (tree *Tree, err error) {
|
||||||
|
inputBytes, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tree, err = LoadBytes(inputBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load creates a Tree from a string.
|
||||||
|
func Load(content string) (tree *Tree, err error) {
|
||||||
|
return LoadBytes([]byte(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFile creates a Tree from a file.
|
||||||
|
func LoadFile(path string) (tree *Tree, err error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return LoadReader(file)
|
||||||
|
}
|
142
vendor/github.com/pelletier/go-toml/tomltree_create.go
generated
vendored
Normal file
142
vendor/github.com/pelletier/go-toml/tomltree_create.go
generated
vendored
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var kindToType = [reflect.String + 1]reflect.Type{
|
||||||
|
reflect.Bool: reflect.TypeOf(true),
|
||||||
|
reflect.String: reflect.TypeOf(""),
|
||||||
|
reflect.Float32: reflect.TypeOf(float64(1)),
|
||||||
|
reflect.Float64: reflect.TypeOf(float64(1)),
|
||||||
|
reflect.Int: reflect.TypeOf(int64(1)),
|
||||||
|
reflect.Int8: reflect.TypeOf(int64(1)),
|
||||||
|
reflect.Int16: reflect.TypeOf(int64(1)),
|
||||||
|
reflect.Int32: reflect.TypeOf(int64(1)),
|
||||||
|
reflect.Int64: reflect.TypeOf(int64(1)),
|
||||||
|
reflect.Uint: reflect.TypeOf(uint64(1)),
|
||||||
|
reflect.Uint8: reflect.TypeOf(uint64(1)),
|
||||||
|
reflect.Uint16: reflect.TypeOf(uint64(1)),
|
||||||
|
reflect.Uint32: reflect.TypeOf(uint64(1)),
|
||||||
|
reflect.Uint64: reflect.TypeOf(uint64(1)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// typeFor returns a reflect.Type for a reflect.Kind, or nil if none is found.
|
||||||
|
// supported values:
|
||||||
|
// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
|
||||||
|
func typeFor(k reflect.Kind) reflect.Type {
|
||||||
|
if k > 0 && int(k) < len(kindToType) {
|
||||||
|
return kindToType[k]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func simpleValueCoercion(object interface{}) (interface{}, error) {
|
||||||
|
switch original := object.(type) {
|
||||||
|
case string, bool, int64, uint64, float64, time.Time:
|
||||||
|
return original, nil
|
||||||
|
case int:
|
||||||
|
return int64(original), nil
|
||||||
|
case int8:
|
||||||
|
return int64(original), nil
|
||||||
|
case int16:
|
||||||
|
return int64(original), nil
|
||||||
|
case int32:
|
||||||
|
return int64(original), nil
|
||||||
|
case uint:
|
||||||
|
return uint64(original), nil
|
||||||
|
case uint8:
|
||||||
|
return uint64(original), nil
|
||||||
|
case uint16:
|
||||||
|
return uint64(original), nil
|
||||||
|
case uint32:
|
||||||
|
return uint64(original), nil
|
||||||
|
case float32:
|
||||||
|
return float64(original), nil
|
||||||
|
case fmt.Stringer:
|
||||||
|
return original.String(), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("cannot convert type %T to Tree", object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceToTree(object interface{}) (interface{}, error) {
|
||||||
|
// arrays are a bit tricky, since they can represent either a
|
||||||
|
// collection of simple values, which is represented by one
|
||||||
|
// *tomlValue, or an array of tables, which is represented by an
|
||||||
|
// array of *Tree.
|
||||||
|
|
||||||
|
// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
|
||||||
|
value := reflect.ValueOf(object)
|
||||||
|
insideType := value.Type().Elem()
|
||||||
|
length := value.Len()
|
||||||
|
if length > 0 {
|
||||||
|
insideType = reflect.ValueOf(value.Index(0).Interface()).Type()
|
||||||
|
}
|
||||||
|
if insideType.Kind() == reflect.Map {
|
||||||
|
// this is considered as an array of tables
|
||||||
|
tablesArray := make([]*Tree, 0, length)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
table := value.Index(i)
|
||||||
|
tree, err := toTree(table.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tablesArray = append(tablesArray, tree.(*Tree))
|
||||||
|
}
|
||||||
|
return tablesArray, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceType := typeFor(insideType.Kind())
|
||||||
|
if sliceType == nil {
|
||||||
|
sliceType = insideType
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)
|
||||||
|
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
val := value.Index(i).Interface()
|
||||||
|
simpleValue, err := simpleValueCoercion(val)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
|
||||||
|
}
|
||||||
|
return &tomlValue{arrayValue.Interface(), Position{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func toTree(object interface{}) (interface{}, error) {
|
||||||
|
value := reflect.ValueOf(object)
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Map {
|
||||||
|
values := map[string]interface{}{}
|
||||||
|
keys := value.MapKeys()
|
||||||
|
for _, key := range keys {
|
||||||
|
if key.Kind() != reflect.String {
|
||||||
|
if _, ok := key.Interface().(string); !ok {
|
||||||
|
return nil, fmt.Errorf("map key needs to be a string, not %T (%v)", key.Interface(), key.Kind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
v := value.MapIndex(key)
|
||||||
|
newValue, err := toTree(v.Interface())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
values[key.String()] = newValue
|
||||||
|
}
|
||||||
|
return &Tree{values, Position{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
|
||||||
|
return sliceToTree(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleValue, err := simpleValueCoercion(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tomlValue{simpleValue, Position{}}, nil
|
||||||
|
}
|
233
vendor/github.com/pelletier/go-toml/tomltree_write.go
generated
vendored
Normal file
233
vendor/github.com/pelletier/go-toml/tomltree_write.go
generated
vendored
Normal file
|
@ -0,0 +1,233 @@
|
||||||
|
package toml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// encodes a string to a TOML-compliant string value
|
||||||
|
func encodeTomlString(value string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
for _, rr := range value {
|
||||||
|
switch rr {
|
||||||
|
case '\b':
|
||||||
|
b.WriteString(`\b`)
|
||||||
|
case '\t':
|
||||||
|
b.WriteString(`\t`)
|
||||||
|
case '\n':
|
||||||
|
b.WriteString(`\n`)
|
||||||
|
case '\f':
|
||||||
|
b.WriteString(`\f`)
|
||||||
|
case '\r':
|
||||||
|
b.WriteString(`\r`)
|
||||||
|
case '"':
|
||||||
|
b.WriteString(`\"`)
|
||||||
|
case '\\':
|
||||||
|
b.WriteString(`\\`)
|
||||||
|
default:
|
||||||
|
intRr := uint16(rr)
|
||||||
|
if intRr < 0x001F {
|
||||||
|
b.WriteString(fmt.Sprintf("\\u%0.4X", intRr))
|
||||||
|
} else {
|
||||||
|
b.WriteRune(rr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tomlValueStringRepresentation(v interface{}) (string, error) {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case uint64:
|
||||||
|
return strconv.FormatUint(value, 10), nil
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(value, 10), nil
|
||||||
|
case float64:
|
||||||
|
// Ensure a round float does contain a decimal point. Otherwise feeding
|
||||||
|
// the output back to the parser would convert to an integer.
|
||||||
|
if math.Trunc(value) == value {
|
||||||
|
return strconv.FormatFloat(value, 'f', 1, 32), nil
|
||||||
|
}
|
||||||
|
return strconv.FormatFloat(value, 'f', -1, 32), nil
|
||||||
|
case string:
|
||||||
|
return "\"" + encodeTomlString(value) + "\"", nil
|
||||||
|
case []byte:
|
||||||
|
b, _ := v.([]byte)
|
||||||
|
return tomlValueStringRepresentation(string(b))
|
||||||
|
case bool:
|
||||||
|
if value {
|
||||||
|
return "true", nil
|
||||||
|
}
|
||||||
|
return "false", nil
|
||||||
|
case time.Time:
|
||||||
|
return value.Format(time.RFC3339), nil
|
||||||
|
case nil:
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rv := reflect.ValueOf(v)
|
||||||
|
|
||||||
|
if rv.Kind() == reflect.Slice {
|
||||||
|
values := []string{}
|
||||||
|
for i := 0; i < rv.Len(); i++ {
|
||||||
|
item := rv.Index(i).Interface()
|
||||||
|
itemRepr, err := tomlValueStringRepresentation(item)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
values = append(values, itemRepr)
|
||||||
|
}
|
||||||
|
return "[" + strings.Join(values, ",") + "]", nil
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("unsupported value type %T: %v", v, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tree) writeTo(w io.Writer, indent, keyspace string, bytesCount int64) (int64, error) {
|
||||||
|
simpleValuesKeys := make([]string, 0)
|
||||||
|
complexValuesKeys := make([]string, 0)
|
||||||
|
|
||||||
|
for k := range t.values {
|
||||||
|
v := t.values[k]
|
||||||
|
switch v.(type) {
|
||||||
|
case *Tree, []*Tree:
|
||||||
|
complexValuesKeys = append(complexValuesKeys, k)
|
||||||
|
default:
|
||||||
|
simpleValuesKeys = append(simpleValuesKeys, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(simpleValuesKeys)
|
||||||
|
sort.Strings(complexValuesKeys)
|
||||||
|
|
||||||
|
for _, k := range simpleValuesKeys {
|
||||||
|
v, ok := t.values[k].(*tomlValue)
|
||||||
|
if !ok {
|
||||||
|
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
|
||||||
|
}
|
||||||
|
|
||||||
|
repr, err := tomlValueStringRepresentation(v.value)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
|
||||||
|
writtenBytesCount, err := writeStrings(w, indent, k, " = ", repr, "\n")
|
||||||
|
bytesCount += int64(writtenBytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range complexValuesKeys {
|
||||||
|
v := t.values[k]
|
||||||
|
|
||||||
|
combinedKey := k
|
||||||
|
if keyspace != "" {
|
||||||
|
combinedKey = keyspace + "." + combinedKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch node := v.(type) {
|
||||||
|
// node has to be of those two types given how keys are sorted above
|
||||||
|
case *Tree:
|
||||||
|
writtenBytesCount, err := writeStrings(w, "\n", indent, "[", combinedKey, "]\n")
|
||||||
|
bytesCount += int64(writtenBytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
bytesCount, err = node.writeTo(w, indent+" ", combinedKey, bytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
case []*Tree:
|
||||||
|
for _, subTree := range node {
|
||||||
|
writtenBytesCount, err := writeStrings(w, "\n", indent, "[[", combinedKey, "]]\n")
|
||||||
|
bytesCount += int64(writtenBytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesCount, err = subTree.writeTo(w, indent+" ", combinedKey, bytesCount)
|
||||||
|
if err != nil {
|
||||||
|
return bytesCount, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeStrings(w io.Writer, s ...string) (int, error) {
|
||||||
|
var n int
|
||||||
|
for i := range s {
|
||||||
|
b, err := io.WriteString(w, s[i])
|
||||||
|
n += b
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo encode the Tree as Toml and writes it to the writer w.
|
||||||
|
// Returns the number of bytes written in case of success, or an error if anything happened.
|
||||||
|
func (t *Tree) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
return t.writeTo(w, "", "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTomlString generates a human-readable representation of the current tree.
|
||||||
|
// Output spans multiple lines, and is suitable for ingest by a TOML parser.
|
||||||
|
// If the conversion cannot be performed, ToString returns a non-nil error.
|
||||||
|
func (t *Tree) ToTomlString() (string, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, err := t.WriteTo(&buf)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String generates a human-readable representation of the current tree.
|
||||||
|
// Alias of ToString. Present to implement the fmt.Stringer interface.
|
||||||
|
func (t *Tree) String() string {
|
||||||
|
result, _ := t.ToTomlString()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToMap recursively generates a representation of the tree using Go built-in structures.
|
||||||
|
// The following types are used:
|
||||||
|
//
|
||||||
|
// * bool
|
||||||
|
// * float64
|
||||||
|
// * int64
|
||||||
|
// * string
|
||||||
|
// * uint64
|
||||||
|
// * time.Time
|
||||||
|
// * map[string]interface{} (where interface{} is any of this list)
|
||||||
|
// * []interface{} (where interface{} is any of this list)
|
||||||
|
func (t *Tree) ToMap() map[string]interface{} {
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
|
||||||
|
for k, v := range t.values {
|
||||||
|
switch node := v.(type) {
|
||||||
|
case []*Tree:
|
||||||
|
var array []interface{}
|
||||||
|
for _, item := range node {
|
||||||
|
array = append(array, item.ToMap())
|
||||||
|
}
|
||||||
|
result[k] = array
|
||||||
|
case *Tree:
|
||||||
|
result[k] = node.ToMap()
|
||||||
|
case *tomlValue:
|
||||||
|
result[k] = node.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
Loading…
Reference in a new issue