From eef48c1d0a71a6a9529170af130551e6402e3eb1 Mon Sep 17 00:00:00 2001 From: Roy Olav Purser Date: Fri, 23 Sep 2022 14:44:08 +0200 Subject: [PATCH] initial work on auto generated definitions --- envconf.go | 113 ++++++++++------------------------------------ generate.py | 106 +++++++++++++++++++++++++++++++++++++++++++ generated_code.go | 25 ++++++++++ 3 files changed, 156 insertions(+), 88 deletions(-) create mode 100644 generate.py create mode 100644 generated_code.go diff --git a/envconf.go b/envconf.go index ea06801..f606437 100644 --- a/envconf.go +++ b/envconf.go @@ -20,18 +20,18 @@ type cEntry struct { hasdef bool // Default value is defined } -type keyLookupStruct struct { +type keyLookupType struct { key string end bool - next map[string]*keyLookupStruct + next map[string]*keyLookupType } -func (kl *keyLookupStruct) setKey(parts []string, key string) { +func (kl *keyLookupType) setKey(parts []string, key string) { if len(kl.next) == 0 { - kl.next = make(map[string]*keyLookupStruct) + kl.next = make(map[string]*keyLookupType) } if len(parts) > 0 { - nextLookup := new(keyLookupStruct) + nextLookup := new(keyLookupType) nextLookup.setKey(parts[1:], key) kl.next[parts[0]] = nextLookup } else { @@ -40,7 +40,7 @@ func (kl *keyLookupStruct) setKey(parts []string, key string) { } } -func (kl *keyLookupStruct) print() { +func (kl *keyLookupType) print() { if kl.end { fmt.Println(kl.key) fmt.Println("-----------------") @@ -52,28 +52,23 @@ func (kl *keyLookupStruct) print() { } } +type mapEnvType [5]*keyLookupType + 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 + parsed bool + env map[string]cEntry + mapEnv mapEnvType } // 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 level, _ := range config.mapEnv { + config.mapEnv[level] = new(keyLookupType) } for _, v := range os.Environ() { @@ -81,7 +76,7 @@ func NewConfig() *Config { if len(splitted) == 2 { key := cleanKey(splitted[0]) val := splitted[1] - splitted = append(strings.Split(key, "_"), make([]string, levels, levels)...) + splitted = append(strings.Split(key, "_"), make([]string, len(config.mapEnv), len(config.mapEnv))...) if unicode.IsLetter(getFirstRune(key)) { var entry cEntry entry.value = val @@ -90,7 +85,7 @@ func NewConfig() *Config { entry.empty = false config.env[key] = entry if len(splitted) > 1 { - for level := 1; level <= levels; level++ { + for level := 1; level <= len(config.mapEnv); level++ { for count := 0; count < level; count++ { parts := make([]string, level, level) parts[0] = strings.Trim(strings.Join(splitted[:count+1], "_"), "_") @@ -106,69 +101,11 @@ func NewConfig() *Config { if parts[len(parts)-1] == "" { break } - config.mapEnvTest[level].setKey(parts, key) + config.mapEnv[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 - } - */ + config.mapEnv[level].print() } } - } } } @@ -196,12 +133,12 @@ func (c *Config) Define(key string, dtype DataType) { // 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] + entries, ok := c.mapEnv[0].next[key] if ok { - for _, key = range entries { - entry := c.env[key] - entry.dtype = dtype - c.env[key] = entry + for _, entry := range entries.next { + defEntry := c.env[entry.key] + defEntry.dtype = dtype + c.env[entry.key] = defEntry } } } @@ -397,11 +334,11 @@ func (c *Config) getRawMap(key string, dtype DataType) (empty map[string]cValue) retval := make(map[string]cValue) if c.parsed { key = cleanKey(key) - entries, ok := c.mapEnv[key] + entries, ok := c.mapEnv[0].next[key] if ok { - for k, v := range entries { - entry := c.env[v] + for k, v := range entries.next { + entry := c.env[v.key] if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { retval[k] = entry.parsed } else { diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..ba851c4 --- /dev/null +++ b/generate.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +import typing + + +def get_return_type(levels: int, dtype: str) -> str: + return ("map[string]" * levels) + dtype + + +def get_signature(levels: int, sig_dtype: str, dtype: str) -> str: + return ( + "func (c *Config) get" + + ("Map" * levels) + + sig_dtype + + "(key string) (empty " + + get_return_type(levels, dtype) + + ") {" + ) + + +def get_empty_def(levels: int, dtype: str) -> str: + return "empty = make(" + get_return_type(levels, dtype) + ")" + + +def get_nested_keys(levels: int) -> str: + txt = "" + for count in range(levels): + txt += "[k" + str(count + 10) + "]" + return txt + + +def get_retval_def(levels: int, current_level: int, dtype: str) -> str: + base = "retval := make(" + if current_level > 0: + base = "retval" + get_nested_keys(current_level) + " = make(" + return base + get_return_type(levels - current_level, dtype) + ")" + + +def get_entries(levels: int) -> str: + return f"v9, ok := c.mapEnv[{levels}].next[key]" + + +def get_entry(levels: int) -> str: + return f"entry := c.env[v{levels}.key]" + + +def get_loop(levels: int) -> str: + return f"for k{levels + 10}, v{levels + 10} := range v{levels + 9}.next {{" + + +def get_function(file: typing.TextIO, levels: int, sig_dtype: str, dtype: str): + print(get_signature(levels, sig_dtype, dtype), file=file) + print(get_empty_def(levels, dtype), file=file) + print(get_retval_def(levels, 0, dtype), file=file) + print("if c.parsed {", file=file) + print("key = cleanKey(key)", file=file) + print(get_entries(levels), file=file) + for level in range(levels): + print(get_loop(level), file=file) + print(get_retval_def(levels, level, dtype), file=file) + print(get_loop(levels), file=file) + print(get_entry(levels), file=file) + print( + "if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) {", + file=file, + ) + print("retval[k] = entry.parsed", file=file) + print("} else {", file=file) + print("return", file=file) + print("}", file=file) + print("}", file=file) + print("return retval", file=file) + print("}", file=file) + print("}", file=file) + print("return", file=file) + print("}", file=file) + + +with open("generated_code.go", mode="w", encoding="utf-8") as f: + print("package envconf", file=f) + get_function(f, 3, "Int", "int64") + + +# +# 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[0].next[key] +# +# if ok { +# for k, v := range entries.next { +# entry := c.env[v.key] +# if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { +# retval[k] = entry.parsed +# } else { +# return +# } +# } +# return retval +# } +# } +# return +# } +# +# diff --git a/generated_code.go b/generated_code.go new file mode 100644 index 0000000..8c72b46 --- /dev/null +++ b/generated_code.go @@ -0,0 +1,25 @@ +func (c *Config) getMapMapMapInt(key string) (empty map[string]map[string]map[string]int64) { +empty = make(map[string]map[string]map[string]int64) +retval := make(map[string]map[string]map[string]int64) +if c.parsed { +key = cleanKey(key) +v9, ok := c.mapEnv[3].next[key] +for k10, v10 := range v9.next { +retval := make(map[string]map[string]map[string]int64) +for k11, v11 := range v10.next { +retval[k10] = make(map[string]map[string]int64) +for k12, v12 := range v11.next { +retval[k10][k11] = make(map[string]int64) +for k13, v13 := range v12.next { +entry := c.env[v3.key] +if (entry.dtype.baseType() == dtype.baseType()) && (entry.parsed.err == nil) { +retval[k] = entry.parsed +} else { +return +} +} +return retval +} +} +return +}