package envconf import ( "errors" "fmt" "os" "regexp" "strings" "time" "unicode" ) type cEntry struct { value string parsed cValue dtype DataType unset bool empty bool defval string // The default value hasdef bool // Default value is defined } type keyLookupStruct struct { key string end bool next map[string]*keyLookupStruct } func (kl *keyLookupStruct) setKey(parts []string, key string) { if len(kl.next) == 0 { kl.next = make(map[string]*keyLookupStruct) } if len(parts) > 0 { nextLookup := new(keyLookupStruct) nextLookup.setKey(parts[1:], key) kl.next[parts[0]] = nextLookup } else { kl.key = key kl.end = true } } func (kl *keyLookupStruct) print() { if kl.end { fmt.Println(kl.key) fmt.Println("-----------------") return } for key, val := range kl.next { fmt.Println(key) val.print() } } type Config struct { parsed bool env map[string]cEntry mapEnvTest map[int]*keyLookupStruct mapEnv map[string]map[string]string mapMapEnv map[string]map[string]map[string]string mapMapMapEnv map[string]map[string]map[string]map[string]string mapMapMapMapEnv map[string]map[string]map[string]map[string]map[string]string mapMapMapMapMapEnv map[string]map[string]map[string]map[string]map[string]map[string]string } // NewConfig returns an envconf.Config that is used to read configuration from environment variables. // The environment variables are stored in envconf.Config, so changes to the environment after NewConfig has been called // will not be taken into account. func NewConfig() *Config { levels := 5 config := new(Config) config.parsed = false config.env = make(map[string]cEntry) config.mapEnvTest = make(map[int]*keyLookupStruct) for level := 1; level <= levels; level++ { config.mapEnvTest[level] = new(keyLookupStruct) } for _, v := range os.Environ() { splitted := strings.SplitN(v, "=", 2) if len(splitted) == 2 { key := cleanKey(splitted[0]) val := splitted[1] splitted = append(strings.Split(key, "_"), make([]string, levels, levels)...) if unicode.IsLetter(getFirstRune(key)) { var entry cEntry entry.value = val entry.dtype = TypeNone entry.unset = false entry.empty = false config.env[key] = entry if len(splitted) > 1 { for level := 1; level <= levels; level++ { for count := 0; count < level; count++ { parts := make([]string, level, level) parts[0] = strings.Trim(strings.Join(splitted[:count+1], "_"), "_") for partPos, _ := range parts { if partPos > 0 { if partPos+1 == len(parts) { parts[partPos] = strings.Trim(strings.Join(splitted[partPos+count:], "_"), "_") } else { parts[partPos] = splitted[partPos+count] } } } if parts[len(parts)-1] == "" { break } config.mapEnvTest[level].setKey(parts, key) } config.mapEnvTest[level].print() /* var p0, p1, p2, p3, p4, p5 string p0 = strings.Trim(strings.Join(splitted[:count], "_"), "_") p1 = strings.Trim(strings.Join(splitted[count:], "_"), "_") if len(config.mapEnv[p0]) == 0 { config.mapEnv[p0] = make(map[string]string) config.mapMapEnv[p0] = make(map[string]map[string]string) config.mapMapMapEnv[p0] = make(map[string]map[string]map[string]string) config.mapMapMapMapEnv[p0] = make(map[string]map[string]map[string]map[string]string) config.mapMapMapMapMapEnv[p0] = make(map[string]map[string]map[string]map[string]map[string]string) } if p0 != "" && p1 != "" { config.mapEnv[p0][p1] = key } p1 = splitted[count] p2 = strings.Trim(strings.Join(splitted[count+1:], "_"), "_") if len(config.mapMapEnv[p0][p1]) == 0 { config.mapMapEnv[p0][p1] = make(map[string]string) config.mapMapMapEnv[p0][p1] = make(map[string]map[string]string) config.mapMapMapMapEnv[p0][p1] = make(map[string]map[string]map[string]string) config.mapMapMapMapMapEnv[p0][p1] = make(map[string]map[string]map[string]map[string]string) } if p0 != "" && p1 != "" && p2 != "" { config.mapMapEnv[p0][p1][p2] = key } p2 = splitted[count+1] p3 = strings.Trim(strings.Join(splitted[count+2:], "_"), "_") if len(config.mapMapMapEnv[p0][p1][p2]) == 0 { config.mapMapMapEnv[p0][p1][p2] = make(map[string]string) config.mapMapMapMapEnv[p0][p1][p2] = make(map[string]map[string]string) config.mapMapMapMapMapEnv[p0][p1][p2] = make(map[string]map[string]map[string]string) } if p0 != "" && p1 != "" && p2 != "" && p3 != "" { config.mapMapMapEnv[p0][p1][p2][p3] = key } p3 = splitted[count+2] p4 = strings.Trim(strings.Join(splitted[count+3:], "_"), "_") if len(config.mapMapMapMapEnv[p0][p1][p2][p3]) == 0 { config.mapMapMapMapEnv[p0][p1][p2][p3] = make(map[string]string) config.mapMapMapMapMapEnv[p0][p1][p2][p3] = make(map[string]map[string]string) } if p0 != "" && p1 != "" && p2 != "" && p3 != "" && p4 != "" { config.mapMapMapMapEnv[p0][p1][p2][p3][p4] = key } p4 = splitted[count+3] p5 = strings.Trim(strings.Join(splitted[count+4:], "_"), "_") if len(config.mapMapMapMapMapEnv[p0][p1][p2][p3][p4]) == 0 { config.mapMapMapMapMapEnv[p0][p1][p2][p3][p4] = make(map[string]string) } if p0 != "" && p1 != "" && p2 != "" && p3 != "" && p4 != "" && p5 != "" { config.mapMapMapMapMapEnv[p0][p1][p2][p3][p4][p5] = key } */ } } } } } return config } // Define the type of an environment variable. // Variables without a defined type will be ignored by Parse. func (c *Config) Define(key string, dtype DataType) { key = cleanKey(key) entry, ok := c.env[key] if ok { entry.dtype = dtype c.env[key] = entry } else { var entry cEntry entry.dtype = dtype entry.unset = true entry.empty = true c.env[key] = entry } } // Define the type of an environment variable. // Variables without a defined type will be ignored by Parse. func (c *Config) DefineMap(key string, dtype DataType) { key = cleanKey(key) entries, ok := c.mapEnv[key] if ok { for _, key = range entries { entry := c.env[key] entry.dtype = dtype c.env[key] = entry } } } // Define the type and default value of an environment variable. // Variables without a defined type will be ignored by Parse. func (c *Config) DefineDefault(key string, val string, dtype DataType) { key = cleanKey(key) entry, ok := c.env[key] if ok { if entry.unset { entry.value = val } entry.dtype = dtype entry.empty = false entry.defval = val entry.hasdef = true c.env[key] = entry } else { var entry cEntry entry.dtype = dtype entry.unset = true entry.empty = false entry.value = val entry.defval = val entry.hasdef = true c.env[key] = entry } } // Parse parses the environment variables previously defined by Define and DefineDefault. // Parse should only be called once for a given envconf.Config. func (c *Config) Parse() { if c.parsed { return } c.parsed = true failed := false for k, v := range c.env { if v.empty { if v.unset { failed = true } } else { v.parsed = v.dtype.parse(k, v.value) c.env[k] = v } } if failed { for k, v := range c.env { if (v.parsed.err == nil) && v.unset { if v.empty { v.parsed.err = errors.New(fmt.Sprintf(`Environment variable "%s" not found. It should have been of type %s.`, k, v.dtype)) c.env[k] = v } else { v.parsed.err = errors.New(fmt.Sprintf(`Environment variable "%s" not found. Default value "%s" of type %s used.`, k, v.value, v.dtype)) c.env[k] = v } } } } } func (c *Config) help() { max := make([]int, 2, 2) preq := false pdef := false for k, v := range c.env { if v.dtype != TypeNone { if len(k) > max[0] { max[0] = len(k) } if len(v.dtype.String()) > max[1] { max[1] = len(v.dtype.String()) } if v.hasdef { pdef = true } else { preq = true } } } if pdef { fmt.Println() for k, v := range c.env { if v.dtype != TypeNone { if v.hasdef { format := fmt.Sprintf("Variable %%-%ds| Type %%-%ds| Default %%s\n", max[0]+5, max[1]+3) fmt.Printf(format, fmt.Sprintf(`"%s"`, k), v.dtype, fmt.Sprintf(`"%s"`, v.defval)) } } } } if preq { fmt.Println() for k, v := range c.env { if v.dtype != TypeNone { if !v.hasdef { format := fmt.Sprintf("Variable %%-%ds| Type %%-%ds| Required\n", max[0]+5, max[1]+3) fmt.Printf(format, fmt.Sprintf(`"%s"`, k), v.dtype) } } } } fmt.Println() } func (c *Config) StatusHelp() bool { flags := make(map[string]bool) flags["--help"] = true flags["-help"] = true flags["-h"] = true if len(os.Args) > 1 { key := strings.ToLower(strings.TrimSpace(os.Args[1])) _, ok := flags[key] if ok { c.help() return false } } return c.Status() } // Status prints out failures that occured while parsing the environment to os.Stderr. // Variables that have been defined without a default value and are // missing from the environment will be considered a failure. // If parsing of any of the variables has failed Status will return false. func (c *Config) Status() (ok bool) { ok = c.parsed if ok { for _, v := range c.env { err := v.parsed.err if err != nil { ok = false if !v.empty { fmt.Fprintln(os.Stderr, err) } } } if !ok { fmt.Fprintln(os.Stderr, "") for _, v := range c.env { err := v.parsed.err if (err != nil) && v.empty { fmt.Fprintln(os.Stderr, err) } } } } return } func cleanKey(key string) string { expr := regexp.MustCompile("__+") fn := func(r rune) rune { if (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || r == '_' { return r } return -1 } key = strings.Trim(strings.Map(fn, strings.ToUpper(key)), "_") return expr.ReplaceAllString(key, "_") } func keySplit(key string) (left string, right string, ok bool) { key = cleanKey(key) pos := strings.LastIndex(key, "_") ok = false if (pos+1 < len(key)) && (pos > 1) { return cleanKey(key[:pos]), cleanKey(key[pos:]), true } return } func (c *Config) getRaw(key string, dtype DataType) (val cValue) { val.dtype = TypeNone val.binval = make([]byte, 0, 0) if c.parsed { key = cleanKey(key) entry, ok := c.env[key] if ok && (entry.dtype.baseType() == dtype.baseType()) { return entry.parsed } } return } func (c *Config) getRawMap(key string, dtype DataType) (empty map[string]cValue) { empty = make(map[string]cValue) retval := make(map[string]cValue) if c.parsed { key = cleanKey(key) entries, ok := c.mapEnv[key] if ok { for k, v := range entries { entry := c.env[v] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k] = entry.parsed } else { return } } return retval } } return } func (c *Config) getRawMapMap(key string, dtype DataType) (empty map[string]map[string]cValue) { empty = make(map[string]map[string]cValue) retval := make(map[string]map[string]cValue) if c.parsed { key = cleanKey(key) entries, ok := c.mapMapEnv[key] if ok { for k1, v1 := range entries { retval[k1] = make(map[string]cValue) for k2, v2 := range v1 { entry := c.env[v2] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k1][k2] = entry.parsed } else { return } } } return retval } } return } func (c *Config) getRawMapMapMap(key string, dtype DataType) (empty map[string]map[string]map[string]cValue) { empty = make(map[string]map[string]map[string]cValue) retval := make(map[string]map[string]map[string]cValue) if c.parsed { key = cleanKey(key) entries, ok := c.mapMapMapEnv[key] if ok { for k1, v1 := range entries { retval[k1] = make(map[string]map[string]cValue) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]cValue) for k3, v3 := range v2 { entry := c.env[v3] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k1][k2][k3] = entry.parsed } else { return } } } } return retval } } return } func (c *Config) getRawMapMapMapMap(key string, dtype DataType) (empty map[string]map[string]map[string]map[string]cValue) { empty = make(map[string]map[string]map[string]map[string]cValue) retval := make(map[string]map[string]map[string]map[string]cValue) if c.parsed { key = cleanKey(key) entries, ok := c.mapMapMapMapEnv[key] if ok { for k1, v1 := range entries { retval[k1] = make(map[string]map[string]map[string]cValue) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]cValue) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]cValue) for k4, v4 := range v3 { entry := c.env[v4] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k1][k2][k3][k4] = entry.parsed } else { return } } } } } return retval } } return } func (c *Config) getRawMapMapMapMapMap(key string, dtype DataType) (empty map[string]map[string]map[string]map[string]map[string]cValue) { empty = make(map[string]map[string]map[string]map[string]map[string]cValue) retval := make(map[string]map[string]map[string]map[string]map[string]cValue) if c.parsed { key = cleanKey(key) entries, ok := c.mapMapMapMapMapEnv[key] if ok { for k1, v1 := range entries { retval[k1] = make(map[string]map[string]map[string]map[string]cValue) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string]cValue) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string]cValue) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string]cValue) for k5, v5 := range v4 { entry := c.env[v5] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k1][k2][k3][k4][k5] = entry.parsed } else { return } } } } } } return retval } } return } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeInt the function will return 0. func (c *Config) GetInt(key string) int64 { val := c.getRaw(key, TypeInt) return val.intval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeHex the function will return []byte{}. func (c *Config) GetHex(key string) []byte { val := c.getRaw(key, TypeHex) return val.binval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeMetric the function will return 0. func (c *Config) GetMetric(key string) int64 { val := c.getRaw(key, TypeMetric) return val.intval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeDirectory the // function will return the empty string. func (c *Config) GetDirectory(key string) string { val := c.getRaw(key, TypeDirectory) return val.strval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeString the // function will return the empty string. func (c *Config) GetString(key string) string { val := c.getRaw(key, TypeString) return val.strval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeDuration the // function will return time.Duration(0). func (c *Config) GetDuration(key string) time.Duration { val := c.getRaw(key, TypeDuration) return val.durval } // Returns the value of an environment variable. // If the variable is not defined as envconf.TypeBool the // function will return false. func (c *Config) GetBool(key string) bool { val := c.getRaw(key, TypeBool) return val.boolval } func (c *Config) GetMapInt(key string) (retval map[string]int64) { retval = make(map[string]int64) for k, v := range c.getRawMap(key, TypeInt) { retval[k] = v.intval } return } func (c *Config) GetMapDuration(key string) (retval map[string]time.Duration) { retval = make(map[string]time.Duration) for k, v := range c.getRawMap(key, TypeDuration) { retval[k] = v.durval } return } func (c *Config) GetMapString(key string) (retval map[string]string) { retval = make(map[string]string) for k, v := range c.getRawMap(key, TypeString) { retval[k] = v.strval } return } func (c *Config) GetMapBool(key string) (retval map[string]bool) { retval = make(map[string]bool) for k, v := range c.getRawMap(key, TypeBool) { retval[k] = v.boolval } return } func (c *Config) GetMapHex(key string) (retval map[string][]byte) { retval = make(map[string][]byte) for k, v := range c.getRawMap(key, TypeHex) { retval[k] = v.binval } return } func (c *Config) GetMapMapInt(key string) (retval map[string]map[string]int64) { retval = make(map[string]map[string]int64) for k1, v1 := range c.getRawMapMap(key, TypeInt) { retval[k1] = make(map[string]int64) for k2, v2 := range v1 { retval[k1][k2] = v2.intval } } return } func (c *Config) GetMapMapDuration(key string) (retval map[string]map[string]time.Duration) { retval = make(map[string]map[string]time.Duration) for k1, v1 := range c.getRawMapMap(key, TypeDuration) { retval[k1] = make(map[string]time.Duration) for k2, v2 := range v1 { retval[k1][k2] = v2.durval } } return } func (c *Config) GetMapMapString(key string) (retval map[string]map[string]string) { retval = make(map[string]map[string]string) for k1, v1 := range c.getRawMapMap(key, TypeString) { retval[k1] = make(map[string]string) for k2, v2 := range v1 { retval[k1][k2] = v2.strval } } return } func (c *Config) GetMapMapBool(key string) (retval map[string]map[string]bool) { retval = make(map[string]map[string]bool) for k1, v1 := range c.getRawMapMap(key, TypeBool) { retval[k1] = make(map[string]bool) for k2, v2 := range v1 { retval[k1][k2] = v2.boolval } } return } func (c *Config) GetMapMapHex(key string) (retval map[string]map[string][]byte) { retval = make(map[string]map[string][]byte) for k1, v1 := range c.getRawMapMap(key, TypeHex) { retval[k1] = make(map[string][]byte) for k2, v2 := range v1 { retval[k1][k2] = v2.binval } } return } func (c *Config) GetMapMapMapInt(key string) (retval map[string]map[string]map[string]int64) { retval = make(map[string]map[string]map[string]int64) for k1, v1 := range c.getRawMapMapMap(key, TypeInt) { retval[k1] = make(map[string]map[string]int64) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]int64) for k3, v3 := range v2 { retval[k1][k2][k3] = v3.intval } } } return } func (c *Config) GetMapMapMapDuration(key string) (retval map[string]map[string]map[string]time.Duration) { retval = make(map[string]map[string]map[string]time.Duration) for k1, v1 := range c.getRawMapMapMap(key, TypeDuration) { retval[k1] = make(map[string]map[string]time.Duration) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]time.Duration) for k3, v3 := range v2 { retval[k1][k2][k3] = v3.durval } } } return } func (c *Config) GetMapMapMapString(key string) (retval map[string]map[string]map[string]string) { retval = make(map[string]map[string]map[string]string) for k1, v1 := range c.getRawMapMapMap(key, TypeString) { retval[k1] = make(map[string]map[string]string) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]string) for k3, v3 := range v2 { retval[k1][k2][k3] = v3.strval } } } return } func (c *Config) GetMapMapMapBool(key string) (retval map[string]map[string]map[string]bool) { retval = make(map[string]map[string]map[string]bool) for k1, v1 := range c.getRawMapMapMap(key, TypeBool) { retval[k1] = make(map[string]map[string]bool) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]bool) for k3, v3 := range v2 { retval[k1][k2][k3] = v3.boolval } } } return } func (c *Config) GetMapMapMapHex(key string) (retval map[string]map[string]map[string][]byte) { retval = make(map[string]map[string]map[string][]byte) for k1, v1 := range c.getRawMapMapMap(key, TypeHex) { retval[k1] = make(map[string]map[string][]byte) for k2, v2 := range v1 { retval[k1][k2] = make(map[string][]byte) for k3, v3 := range v2 { retval[k1][k2][k3] = v3.binval } } } return } func (c *Config) GetMapMapMapMapInt(key string) (retval map[string]map[string]map[string]map[string]int64) { retval = make(map[string]map[string]map[string]map[string]int64) for k1, v1 := range c.getRawMapMapMapMap(key, TypeInt) { retval[k1] = make(map[string]map[string]map[string]int64) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]int64) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]int64) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = v4.intval } } } } return } func (c *Config) GetMapMapMapMapDuration(key string) (retval map[string]map[string]map[string]map[string]time.Duration) { retval = make(map[string]map[string]map[string]map[string]time.Duration) for k1, v1 := range c.getRawMapMapMapMap(key, TypeDuration) { retval[k1] = make(map[string]map[string]map[string]time.Duration) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]time.Duration) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]time.Duration) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = v4.durval } } } } return } func (c *Config) GetMapMapMapMapString(key string) (retval map[string]map[string]map[string]map[string]string) { retval = make(map[string]map[string]map[string]map[string]string) for k1, v1 := range c.getRawMapMapMapMap(key, TypeString) { retval[k1] = make(map[string]map[string]map[string]string) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]string) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]string) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = v4.strval } } } } return } func (c *Config) GetMapMapMapMapBool(key string) (retval map[string]map[string]map[string]map[string]bool) { retval = make(map[string]map[string]map[string]map[string]bool) for k1, v1 := range c.getRawMapMapMapMap(key, TypeBool) { retval[k1] = make(map[string]map[string]map[string]bool) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]bool) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]bool) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = v4.boolval } } } } return } func (c *Config) GetMapMapMapMapHex(key string) (retval map[string]map[string]map[string]map[string][]byte) { retval = make(map[string]map[string]map[string]map[string][]byte) for k1, v1 := range c.getRawMapMapMapMap(key, TypeHex) { retval[k1] = make(map[string]map[string]map[string][]byte) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string][]byte) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string][]byte) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = v4.binval } } } } return } func (c *Config) GetMapMapMapMapMapInt(key string) (retval map[string]map[string]map[string]map[string]map[string]int64) { retval = make(map[string]map[string]map[string]map[string]map[string]int64) for k1, v1 := range c.getRawMapMapMapMapMap(key, TypeInt) { retval[k1] = make(map[string]map[string]map[string]map[string]int64) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string]int64) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string]int64) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string]int64) for k5, v5 := range v4 { retval[k1][k2][k3][k4][k5] = v5.intval } } } } } return } func (c *Config) GetMapMapMapMapMapDuration(key string) (retval map[string]map[string]map[string]map[string]map[string]time.Duration) { retval = make(map[string]map[string]map[string]map[string]map[string]time.Duration) for k1, v1 := range c.getRawMapMapMapMapMap(key, TypeDuration) { retval[k1] = make(map[string]map[string]map[string]map[string]time.Duration) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string]time.Duration) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string]time.Duration) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string]time.Duration) for k5, v5 := range v4 { retval[k1][k2][k3][k4][k5] = v5.durval } } } } } return } func (c *Config) GetMapMapMapMapMapString(key string) (retval map[string]map[string]map[string]map[string]map[string]string) { retval = make(map[string]map[string]map[string]map[string]map[string]string) for k1, v1 := range c.getRawMapMapMapMapMap(key, TypeString) { retval[k1] = make(map[string]map[string]map[string]map[string]string) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string]string) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string]string) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string]string) for k5, v5 := range v4 { retval[k1][k2][k3][k4][k5] = v5.strval } } } } } return } func (c *Config) GetMapMapMapMapMapBool(key string) (retval map[string]map[string]map[string]map[string]map[string]bool) { retval = make(map[string]map[string]map[string]map[string]map[string]bool) for k1, v1 := range c.getRawMapMapMapMapMap(key, TypeBool) { retval[k1] = make(map[string]map[string]map[string]map[string]bool) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string]bool) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string]bool) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string]bool) for k5, v5 := range v4 { retval[k1][k2][k3][k4][k5] = v5.boolval } } } } } return } func (c *Config) GetMapMapMapMapMapHex(key string) (retval map[string]map[string]map[string]map[string]map[string][]byte) { retval = make(map[string]map[string]map[string]map[string]map[string][]byte) for k1, v1 := range c.getRawMapMapMapMapMap(key, TypeHex) { retval[k1] = make(map[string]map[string]map[string]map[string][]byte) for k2, v2 := range v1 { retval[k1][k2] = make(map[string]map[string]map[string][]byte) for k3, v3 := range v2 { retval[k1][k2][k3] = make(map[string]map[string][]byte) for k4, v4 := range v3 { retval[k1][k2][k3][k4] = make(map[string][]byte) for k5, v5 := range v4 { retval[k1][k2][k3][k4][k5] = v5.binval } } } } } return } func getFirstRune(str string) rune { for _, v := range str { return v } return rune(0) }