Fix: Vendor missing libraries

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2017-08-04 21:18:39 +02:00
parent ade0951d9d
commit c83bd95be8
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
35 changed files with 5223 additions and 0 deletions

25
Godeps/Godeps.json generated
View File

@ -26,6 +26,31 @@
"ImportPath": "github.com/gorilla/mux",
"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",
"Comment": "v1.1.0-8-g5bf94b6",

19
vendor/github.com/nicksnyder/go-i18n/LICENSE generated vendored Normal file
View 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.

View 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
View 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
}

View 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)
}

View 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
}

View 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)
}

View 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
}

View 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
},
})
}

View 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{})

View 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{})

View 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{})

View 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
View File

@ -0,0 +1 @@
test_program/test_program_bin

23
vendor/github.com/pelletier/go-toml/.travis.yml generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}