// main is the composition root for the application. // It parses config, wires dependencies into an app struct, and delegates. // Implementation details lives in internal/; cmd/ should be kept thin by // deliberately operating only with high level concepts. package main import ( "fmt" "io" "os" "path/filepath" "gitea.djmil.dev/go/template/internal/buildinfo" "gitea.djmil.dev/go/template/internal/greeter" "gitea.djmil.dev/go/template/pkg/logger" "gitea.djmil.dev/go/template/pkg/result" ) // app holds all wired dependencies for the lifetime of the process. type app struct { cfg *Config log *logger.Logger greeter *greeter.Service } func newApp(cfg *Config) *app { // Open the debug writer if requested. The OS closes it on exit. var debugOut io.Writer if cfg.Logger.LogDump != "" { debugOut = result.Of(os.Create(cfg.Logger.LogDump)).Expect("enable logs dump") // #nosec G304 — CLI flag } log := logger.NewCLI(cfg.Logger.Level, debugOut).Expect("create logger") log.Debug("config", "port", cfg.App.Port, "level", cfg.Logger.Level, "env", cfg.App.Env) if cfg.App.Env == "dev" { log.Warn("dev mode — not for production") } return &app{ cfg: cfg, log: log, greeter: greeter.New(log), } } func main() { conf := parseArgs() if conf.Version { fmt.Println(buildinfo.String()) return } app := newApp(conf) app.log.WithFields(map[string]any{ "app": filepath.Base(os.Args[0]), "env": conf.App.Env, }).Info("starting up") if err := result.Run(app.run); err != nil { fmt.Fprintf(os.Stderr, "[failed] %v\n", err) if stack := result.StackTrace(err); stack != "" { fmt.Fprintf(os.Stderr, "%s\n", stack) } os.Exit(1) } } func (a *app) run() { // Warm up the greeter with a few names before the real call. // In debug mode this produces repeated identical debug lines from the greeter, // demonstrating how the deduplication counter collapses them in-place. for _, name := range []string{"Alice", "Bob", "Carol", "Dave"} { a.greeter.Greet(name) } a.showGreeting(a.cfg.Greeter.Name) fmt.Printf("TODO: implement listening on port %d\n", a.cfg.App.Port) } func (a *app) showGreeting(name string) { msg := a.greeter.Greet(name).Expect("greeting") a.log.WithField("message", msg).Info("greeting complete") fmt.Println(msg) }