snapshot of https://github.com/centrifugal/centrifugo.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
test / Lint (push) Failing after 3s
test / Test with Go 1.26.8 (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:17:21 +05:00
commit b352a9a30d
472 changed files with 108375 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
Copyright (c) 2013 Kelsey Hightower
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,2 @@
Kelsey Hightower kelsey.hightower@gmail.com github.com/kelseyhightower
Travis Parker travis.parker@gmail.com github.com/teepark

View File

@@ -0,0 +1,5 @@
# envconfig
This is a fork of https://github.com/kelseyhightower/envconfig to make it fit Centrifugo, original license left unchanged.
There are changes to have access to VarInfo and to avoid using defaults for non-zero values.

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the MIT License that can be found in
// the LICENSE file.
// Package envconfig implements decoding of environment variables based on a user
// defined specification. A typical use is using environment variables for
// configuration settings.
package envconfig

View File

@@ -0,0 +1,7 @@
//go:build appengine || go1.5
package envconfig
import "os"
var lookupEnv = os.LookupEnv

View File

@@ -0,0 +1,8 @@
//go:build !appengine && !go1.5
// +build !appengine,!go1.5
package envconfig
import "syscall"
var lookupEnv = syscall.Getenv

View File

@@ -0,0 +1,425 @@
// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the MIT License that can be found in
// the LICENSE file.
package envconfig
import (
"encoding"
"errors"
"fmt"
"os"
"reflect"
"regexp"
"slices"
"strconv"
"strings"
"time"
)
// ErrInvalidSpecification indicates that a specification is of the wrong type.
var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)")
var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)")
// A ParseError occurs when an environment variable cannot be converted to
// the type required by a struct field during assignment.
type ParseError struct {
KeyName string
FieldName string
TypeName string
Value string
Err error
}
// Decoder has the same semantics as Setter, but takes higher precedence.
// It is provided for historical compatibility.
type Decoder interface {
Decode(value string) error
}
// Setter is implemented by types can self-deserialize values.
// Any type that implements flag.Value also implements Setter.
type Setter interface {
Set(value string) error
}
func (e *ParseError) Error() string {
return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s. details: %[5]s", e.KeyName, e.FieldName, e.Value, e.TypeName, e.Err)
}
// VarInfo maintains information about the configuration variable
type VarInfo struct {
Name string
Alt string
Key string
NestedKey string
Field reflect.Value
Tags reflect.StructTag
NonZeroInFile bool
}
// GatherInfo gathers information about the specified struct
func gatherInfo(prefix string, spec interface{}) ([]VarInfo, error) {
s := reflect.ValueOf(spec)
if s.Kind() != reflect.Pointer {
return nil, ErrInvalidSpecification
}
s = s.Elem()
if s.Kind() != reflect.Struct {
return nil, ErrInvalidSpecification
}
typeOfSpec := s.Type()
// over allocate an info array, we will extend if needed later
infos := make([]VarInfo, 0, s.NumField())
for i := 0; i < s.NumField(); i++ {
f := s.Field(i)
ftype := typeOfSpec.Field(i)
if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) {
continue
}
for f.Kind() == reflect.Pointer {
if f.IsNil() {
if f.Type().Elem().Kind() != reflect.Struct {
// nil pointer to a non-struct: leave it alone
break
}
// nil pointer to struct: create a zero instance
f.Set(reflect.New(f.Type().Elem()))
}
f = f.Elem()
}
name := strings.ToUpper(ftype.Tag.Get("envconfig"))
if name == "" {
name = ftype.Name
}
// Capture information about the config variable
info := VarInfo{
Name: name,
Field: f,
Tags: ftype.Tag,
//Alt: strings.ToUpper(ftype.Tag.Get("envconfig")),
NonZeroInFile: !f.IsZero(),
}
// Default to the field name as the env var name (will be upcased)
info.Key = info.Name
// Best effort to un-pick camel casing as separate words
if isTrue(ftype.Tag.Get("split_words")) {
words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1)
if len(words) > 0 {
var name []string
for _, words := range words {
if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 {
name = append(name, m[1], m[2])
} else {
name = append(name, words[0])
}
}
info.Key = strings.Join(name, "_")
}
}
if info.Alt != "" {
info.Key = info.Alt
}
if prefix != "" {
info.Key = fmt.Sprintf("%s_%s", prefix, info.Key)
}
info.Key = strings.ToUpper(info.Key)
// Construct NestedKey: lowercase, without prefix.
info.NestedKey = strings.ToLower(info.Name)
infos = append(infos, info)
if f.Kind() == reflect.Struct {
// honor Decode if present
if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil {
innerPrefix := prefix
if !ftype.Anonymous {
innerPrefix = info.Key
}
embeddedPtr := f.Addr().Interface()
embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr)
if err != nil {
return nil, err
}
// Update NestedKey for embedded fields.
for i := range embeddedInfos {
if !ftype.Anonymous {
nestedPrefix := strings.ToLower(info.Name)
embeddedInfos[i].NestedKey = nestedPrefix + "." + embeddedInfos[i].NestedKey
}
}
infos = append(infos[:len(infos)-1], embeddedInfos...)
continue
}
}
}
return infos, nil
}
// CheckDisallowed checks that no environment variables with the prefix are set
// that we don't know how or want to parse. This is likely only meaningful with
// a non-empty prefix.
func CheckDisallowed(prefix string, spec interface{}) error {
infos, err := gatherInfo(prefix, spec)
if err != nil {
return err
}
vars := make(map[string]struct{})
for _, info := range infos {
vars[info.Key] = struct{}{}
}
if prefix != "" {
prefix = strings.ToUpper(prefix) + "_"
}
for _, env := range os.Environ() {
if !strings.HasPrefix(env, prefix) {
continue
}
v := strings.SplitN(env, "=", 2)[0]
if _, found := vars[v]; !found {
return fmt.Errorf("unknown environment variable %s", v)
}
}
return nil
}
// Process populates the specified struct based on environment variables
func Process(prefix string, spec interface{}) ([]VarInfo, error) {
return ProcessWithoutNestedKeys(prefix, spec, nil)
}
// ProcessWithoutNestedKeys populates the specified struct based on environment variables
// but excludes fields whose NestedKey is in the excludeKeys list
func ProcessWithoutNestedKeys(prefix string, spec interface{}, excludeKeys []string) ([]VarInfo, error) {
infos, err := gatherInfo(prefix, spec)
for _, info := range infos {
// Skip processing if this key is excluded.
if slices.Contains(excludeKeys, info.NestedKey) {
continue
}
// `os.Getenv` cannot differentiate between an explicitly set empty value
// and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`,
// but it is only available in go1.5 or newer. We're using Go build tags
// here to use os.LookupEnv for >=go1.5
value, ok := lookupEnv(info.Key)
if !ok && info.Alt != "" {
value, ok = lookupEnv(info.Alt)
}
var defaultIsUsed bool
def := info.Tags.Get("default")
if def != "" && (!ok || value == "") { // Empty values are considered unset.
value = def
defaultIsUsed = true
}
req := info.Tags.Get("required")
if !ok && def == "" {
if isTrue(req) {
key := info.Key
if info.Alt != "" {
key = info.Alt
}
return nil, fmt.Errorf("required key %s missing value", key)
}
continue
}
err = processField(value, info.Field, defaultIsUsed)
if err != nil {
return nil, &ParseError{
KeyName: info.Key,
FieldName: info.Name,
TypeName: info.Field.Type().String(),
Value: value,
Err: err,
}
}
}
return infos, err
}
// MustProcess is the same as Process but panics if an error occurs
func MustProcess(prefix string, spec interface{}) {
if _, err := Process(prefix, spec); err != nil {
panic(err)
}
}
const SliceSep = " "
func processField(value string, field reflect.Value, defaultIsUsed bool) error {
// If the value is default but field is not zero already, we don't want to overwrite it.
if defaultIsUsed && !field.IsZero() {
return nil
}
if value == "" {
return nil
}
typ := field.Type()
decoder := decoderFrom(field)
if decoder != nil {
return decoder.Decode(value)
}
// look for Set method if Decode not defined
setter := setterFrom(field)
if setter != nil {
return setter.Set(value)
}
if t := textUnmarshaler(field); t != nil {
return t.UnmarshalText([]byte(value))
}
if b := binaryUnmarshaler(field); b != nil {
return b.UnmarshalBinary([]byte(value))
}
if typ.Kind() == reflect.Pointer {
typ = typ.Elem()
if field.IsNil() {
field.Set(reflect.New(typ))
}
field = field.Elem()
}
switch typ.Kind() {
case reflect.String:
field.SetString(value)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
var (
val int64
err error
)
if field.Kind() == reflect.Int64 && typ.Name() == "Duration" {
var d time.Duration
d, err = time.ParseDuration(value)
val = int64(d)
} else {
val, err = strconv.ParseInt(value, 0, typ.Bits())
}
if err != nil {
return err
}
field.SetInt(val)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
val, err := strconv.ParseUint(value, 0, typ.Bits())
if err != nil {
return err
}
field.SetUint(val)
case reflect.Bool:
val, err := strconv.ParseBool(value)
if err != nil {
return err
}
field.SetBool(val)
case reflect.Float32, reflect.Float64:
val, err := strconv.ParseFloat(value, typ.Bits())
if err != nil {
return err
}
field.SetFloat(val)
case reflect.Slice:
sl := reflect.MakeSlice(typ, 0, 0)
if typ.Elem().Kind() == reflect.Uint8 {
sl = reflect.ValueOf([]byte(value))
} else if strings.TrimSpace(value) != "" {
vals := strings.Split(value, SliceSep)
sl = reflect.MakeSlice(typ, len(vals), len(vals))
for i, val := range vals {
err := processField(val, sl.Index(i), defaultIsUsed)
if err != nil {
return err
}
}
}
field.Set(sl)
case reflect.Map:
mp := reflect.MakeMap(typ)
if strings.TrimSpace(value) != "" {
pairs := strings.Split(value, ",")
for _, pair := range pairs {
kvpair := strings.Split(pair, ":")
if len(kvpair) != 2 {
return fmt.Errorf("invalid map item: %q", pair)
}
k := reflect.New(typ.Key()).Elem()
err := processField(kvpair[0], k, defaultIsUsed)
if err != nil {
return err
}
v := reflect.New(typ.Elem()).Elem()
err = processField(kvpair[1], v, defaultIsUsed)
if err != nil {
return err
}
mp.SetMapIndex(k, v)
}
}
field.Set(mp)
}
return nil
}
func interfaceFrom(field reflect.Value, fn func(interface{}, *bool)) {
// it may be impossible for a struct field to fail this check
if !field.CanInterface() {
return
}
var ok bool
fn(field.Interface(), &ok)
if !ok && field.CanAddr() {
fn(field.Addr().Interface(), &ok)
}
}
func decoderFrom(field reflect.Value) (d Decoder) {
interfaceFrom(field, func(v interface{}, ok *bool) { d, *ok = v.(Decoder) })
return d
}
func setterFrom(field reflect.Value) (s Setter) {
interfaceFrom(field, func(v interface{}, ok *bool) { s, *ok = v.(Setter) })
return s
}
func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) {
interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) })
return t
}
func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) {
interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) })
return b
}
func isTrue(s string) bool {
b, _ := strconv.ParseBool(s)
return b
}

View File

@@ -0,0 +1,68 @@
//go:build go1.8
package envconfig
import (
"errors"
"net/url"
"os"
"testing"
)
type SpecWithURL struct {
UrlValue url.URL
UrlPointer *url.URL
}
func TestParseURL(t *testing.T) {
var s SpecWithURL
os.Clearenv()
_ = os.Setenv("ENV_CONFIG_URLVALUE", "https://github.com/kelseyhightower/envconfig")
_ = os.Setenv("ENV_CONFIG_URLPOINTER", "https://github.com/kelseyhightower/envconfig")
_, err := Process("env_config", &s)
if err != nil {
t.Fatal("unexpected error:", err)
}
u, err := url.Parse("https://github.com/kelseyhightower/envconfig")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if s.UrlValue != *u {
t.Errorf("expected %q, got %q", u, s.UrlValue.String())
}
if *s.UrlPointer != *u {
t.Errorf("expected %q, got %q", u, s.UrlPointer)
}
}
func TestParseURLError(t *testing.T) {
var s SpecWithURL
os.Clearenv()
_ = os.Setenv("ENV_CONFIG_URLPOINTER", "http_://foo")
_, err := Process("env_config", &s)
v, ok := err.(*ParseError)
if !ok {
t.Fatalf("expected ParseError, got %T %v", err, err)
}
if v.FieldName != "UrlPointer" {
t.Errorf("expected %s, got %v", "UrlPointer", v.FieldName)
}
expectedUnderlyingError := url.Error{
Op: "parse",
URL: "http_://foo",
Err: errors.New("first path segment in URL cannot contain colon"),
}
if v.Err.Error() != expectedUnderlyingError.Error() {
t.Errorf("expected %q, got %q", expectedUnderlyingError, v.Err)
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
ENV_CONFIG_ENABLED=some.embedded.value
ENV_CONFIG_EMBEDDEDPORT=
ENV_CONFIG_MULTIWORDVAR=
ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT=
ENV_CONFIG_EMBEDDED_WITH_ALT=
ENV_CONFIG_DEBUG=
ENV_CONFIG_PORT=
ENV_CONFIG_RATE=
ENV_CONFIG_USER=
ENV_CONFIG_TTL=
ENV_CONFIG_TIMEOUT=
ENV_CONFIG_ADMINUSERS=
ENV_CONFIG_MAGICNUMBERS=
ENV_CONFIG_EMPTYNUMBERS=
ENV_CONFIG_BYTESLICE=
ENV_CONFIG_COLORCODES=
ENV_CONFIG_MULTIWORDVAR=
ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT=
ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT=
ENV_CONFIG_SOMEPOINTER=
ENV_CONFIG_SOMEPOINTERWITHDEFAULT=foorbar.is.the.word
ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT=what.alt
ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT=
ENV_CONFIG_SERVICE_HOST=
ENV_CONFIG_DEFAULTVAR=
ENV_CONFIG_REQUIREDVAR=
ENV_CONFIG_BROKER=
ENV_CONFIG_REQUIREDDEFAULT=
ENV_CONFIG_OUTER_INNER=
ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT=
ENV_CONFIG_AFTERNESTED=
ENV_CONFIG_HONOR=
ENV_CONFIG_DATETIME=
ENV_CONFIG_MAPFIELD=
ENV_CONFIG_URLVALUE=
ENV_CONFIG_URLPOINTER=

View File

@@ -0,0 +1,183 @@
This.application.is.configured.via.the.environment..The.following.environment
variables.can.be.used:
ENV_CONFIG_ENABLED
..[description].some.embedded.value
..[type]........True.or.False
..[default].....
..[required]....
ENV_CONFIG_EMBEDDEDPORT
..[description].
..[type]........Integer
..[default].....
..[required]....
ENV_CONFIG_MULTIWORDVAR
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_EMBEDDED_WITH_ALT
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_DEBUG
..[description].
..[type]........True.or.False
..[default].....
..[required]....
ENV_CONFIG_PORT
..[description].
..[type]........Integer
..[default].....
..[required]....
ENV_CONFIG_RATE
..[description].
..[type]........Float
..[default].....
..[required]....
ENV_CONFIG_USER
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_TTL
..[description].
..[type]........Unsigned.Integer
..[default].....
..[required]....
ENV_CONFIG_TIMEOUT
..[description].
..[type]........Duration
..[default].....
..[required]....
ENV_CONFIG_ADMINUSERS
..[description].
..[type]........Comma-separated.list.of.String
..[default].....
..[required]....
ENV_CONFIG_MAGICNUMBERS
..[description].
..[type]........Comma-separated.list.of.Integer
..[default].....
..[required]....
ENV_CONFIG_EMPTYNUMBERS
..[description].
..[type]........Comma-separated.list.of.Integer
..[default].....
..[required]....
ENV_CONFIG_BYTESLICE
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_COLORCODES
..[description].
..[type]........Comma-separated.list.of.String:Integer.pairs
..[default].....
..[required]....
ENV_CONFIG_MULTIWORDVAR
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT
..[description].
..[type]........Unsigned.Integer
..[default].....
..[required]....
ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT
..[description].
..[type]........Unsigned.Integer
..[default].....
..[required]....
ENV_CONFIG_SOMEPOINTER
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_SOMEPOINTERWITHDEFAULT
..[description].foorbar.is.the.word
..[type]........String
..[default].....foo2baz
..[required]....
ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT
..[description].what.alt
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_SERVICE_HOST
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_DEFAULTVAR
..[description].
..[type]........String
..[default].....foobar
..[required]....
ENV_CONFIG_REQUIREDVAR
..[description].
..[type]........String
..[default].....
..[required]....true
ENV_CONFIG_BROKER
..[description].
..[type]........String
..[default].....127.0.0.1
..[required]....
ENV_CONFIG_REQUIREDDEFAULT
..[description].
..[type]........String
..[default].....foo2bar
..[required]....true
ENV_CONFIG_OUTER_INNER
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT
..[description].
..[type]........String
..[default].....fuzzybydefault
..[required]....
ENV_CONFIG_AFTERNESTED
..[description].
..[type]........String
..[default].....
..[required]....
ENV_CONFIG_HONOR
..[description].
..[type]........HonorDecodeInStruct
..[default].....
..[required]....
ENV_CONFIG_DATETIME
..[description].
..[type]........Time
..[default].....
..[required]....
ENV_CONFIG_MAPFIELD
..[description].
..[type]........Comma-separated.list.of.String:String.pairs
..[default].....one:two,three:four
..[required]....
ENV_CONFIG_URLVALUE
..[description].
..[type]........CustomURL
..[default].....
..[required]....
ENV_CONFIG_URLPOINTER
..[description].
..[type]........CustomURL
..[default].....
..[required]....

View File

@@ -0,0 +1,40 @@
This.application.is.configured.via.the.environment..The.following.environment
variables.can.be.used:
KEY..............................................TYPE............................................DEFAULT...............REQUIRED....DESCRIPTION
ENV_CONFIG_ENABLED...............................True.or.False.....................................................................some.embedded.value
ENV_CONFIG_EMBEDDEDPORT..........................Integer...........................................................................
ENV_CONFIG_MULTIWORDVAR..........................String............................................................................
ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT..............String............................................................................
ENV_CONFIG_EMBEDDED_WITH_ALT.....................String............................................................................
ENV_CONFIG_DEBUG.................................True.or.False.....................................................................
ENV_CONFIG_PORT..................................Integer...........................................................................
ENV_CONFIG_RATE..................................Float.............................................................................
ENV_CONFIG_USER..................................String............................................................................
ENV_CONFIG_TTL...................................Unsigned.Integer..................................................................
ENV_CONFIG_TIMEOUT...............................Duration..........................................................................
ENV_CONFIG_ADMINUSERS............................Comma-separated.list.of.String....................................................
ENV_CONFIG_MAGICNUMBERS..........................Comma-separated.list.of.Integer...................................................
ENV_CONFIG_EMPTYNUMBERS..........................Comma-separated.list.of.Integer...................................................
ENV_CONFIG_BYTESLICE.............................String............................................................................
ENV_CONFIG_COLORCODES............................Comma-separated.list.of.String:Integer.pairs......................................
ENV_CONFIG_MULTIWORDVAR..........................String............................................................................
ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT........Unsigned.Integer..................................................................
ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT........Unsigned.Integer..................................................................
ENV_CONFIG_SOMEPOINTER...........................String............................................................................
ENV_CONFIG_SOMEPOINTERWITHDEFAULT................String..........................................foo2baz...........................foorbar.is.the.word
ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT...............String............................................................................what.alt
ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT....String............................................................................
ENV_CONFIG_SERVICE_HOST..........................String............................................................................
ENV_CONFIG_DEFAULTVAR............................String..........................................foobar............................
ENV_CONFIG_REQUIREDVAR...........................String................................................................true........
ENV_CONFIG_BROKER................................String..........................................127.0.0.1.........................
ENV_CONFIG_REQUIREDDEFAULT.......................String..........................................foo2bar...............true........
ENV_CONFIG_OUTER_INNER...........................String............................................................................
ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT.............String..........................................fuzzybydefault....................
ENV_CONFIG_AFTERNESTED...........................String............................................................................
ENV_CONFIG_HONOR.................................HonorDecodeInStruct...............................................................
ENV_CONFIG_DATETIME..............................Time..............................................................................
ENV_CONFIG_MAPFIELD..............................Comma-separated.list.of.String:String.pairs.....one:two,three:four................
ENV_CONFIG_URLVALUE..............................CustomURL.........................................................................
ENV_CONFIG_URLPOINTER............................CustomURL.........................................................................

View File

@@ -0,0 +1,36 @@
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}
{.Key}