From 4c4d92478ddcada543fb427e5cb13c7d5c7b762a Mon Sep 17 00:00:00 2001 From: Bel LaPointe <153096461+breel-render@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:36:23 -0600 Subject: [PATCH] to Config struct for configging --- config.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ config_test.go | 23 +++++++++++++++++ main.go | 41 ++++++++++++++++++++++++------ 3 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 config.go create mode 100644 config_test.go diff --git a/config.go b/config.go new file mode 100644 index 0000000..c9f7c15 --- /dev/null +++ b/config.go @@ -0,0 +1,68 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "regexp" + "slices" + "strconv" + "strings" +) + +type Config struct { + Port int + AB string +} + +func newConfig() (Config, error) { + return newConfigFromEnv(os.Getenv) +} + +func newConfigFromEnv(getEnv func(string) string) (Config, error) { + def := Config{ + Port: 8080, + } + + var m map[string]any + if b, err := json.Marshal(def); err != nil { + return Config{}, err + } else if err := json.Unmarshal(b, &m); err != nil { + return Config{}, err + } + re := regexp.MustCompile(`[A-Z]`) + + for k, v := range m { + envK := k + idxes := re.FindAllIndex([]byte(envK), -1) + slices.Reverse(idxes) + for _, idx := range idxes { + if idx[0] > 0 { + envK = fmt.Sprintf("%s_%s", envK[:idx[0]], envK[idx[0]:]) + } + } + envK = strings.ToUpper(envK) + s := getEnv(envK) + if s == "" { + continue + } + switch v.(type) { + case string: + m[k] = s + case int64, float64: + n, err := strconv.ParseFloat(s, 32) + if err != nil { + return Config{}, err + } + m[k] = n + } + } + + var result Config + if b, err := json.Marshal(m); err != nil { + return Config{}, err + } else if err := json.Unmarshal(b, &result); err != nil { + return Config{}, err + } + return result, nil +} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..b113eee --- /dev/null +++ b/config_test.go @@ -0,0 +1,23 @@ +package main + +import "testing" + +func TestNewConfig(t *testing.T) { + if got, err := newConfigFromEnv(func(k string) string { + t.Logf("getenv(%s)", k) + switch k { + case "PORT": + return "1" + case "A_B": + return "2" + default: + return "" + } + }); err != nil { + t.Fatal(err) + } else if got.Port != 1 { + t.Error(got) + } else if got.AB != "2" { + t.Error(got) + } +} diff --git a/main.go b/main.go index 73571b8..43eb62f 100644 --- a/main.go +++ b/main.go @@ -1,19 +1,46 @@ package main import ( + "context" "fmt" "net/http" - "os" + "os/signal" + "syscall" ) func main() { - p := os.Getenv("PORT") - if p == "" { - p = "8080" - } - addr := fmt.Sprintf(":%s", p) + ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT) + defer can() - if err := http.ListenAndServe(addr, http.HandlerFunc(http.NotFound)); err != nil { + cfg, err := newConfig() + if err != nil { + panic(err) + } + + if err := run(ctx, cfg); err != nil && ctx.Err() == nil { panic(err) } } + +func run(ctx context.Context, cfg Config) error { + select { + case <-ctx.Done(): + return ctx.Err() + case err := <-listenAndServe(ctx, cfg): + return err + } +} + +func listenAndServe(ctx context.Context, cfg Config) chan error { + s := http.Server{ + Addr: fmt.Sprintf(":%d", cfg.Port), + } + + errc := make(chan error) + go func() { + defer close(errc) + errc <- s.ListenAndServe() + }() + + return errc +}