package main import ( "context" "flag" "fmt" "io" "log" "time" "code.osinet.fr/fgm/izidic" "github.com/dcowgill/envflag" "code.osinet.fr/fgm/lbc/internal" "code.osinet.fr/fgm/lbc/web" ) // containers wraps an [izidic.Container] to provide typed services accessors, // obviating the need to type-assert calls to Service. type container struct { *izidic.Container } func (c *container) Config() *web.Config { return c.MustService("config").(*web.Config) } func (c *container) Logger() *log.Logger { return c.MustService("logger").(*log.Logger) } func (c *container) Stats() *web.Stats { return c.MustService("stats").(*web.Stats) } func (c *container) Web() *web.Server { return c.MustService("web").(*web.Server) } // 12-factor/XI: log to stdout. func loggerService(dic *izidic.Container) (any, error) { sw, err := dic.Param("writer") if err != nil { return nil, err } w, ok := sw.(io.Writer) if !ok { return nil, fmt.Errorf("incorrect type for service writer: %T", sw) } logger := log.New(w, "", log.LstdFlags) // Support code not able to use an injected logger log.SetOutput(w) return logger, nil } func configService(dic *izidic.Container) (any, error) { name := dic.MustParam("name").(string) // Provide fallback configuration. c := &web.Config{ Config: internal.Config{ Verbose: false, }, Addr: ":8080", Base: "/", WriteTimeout: 100 * time.Millisecond, } // Build runtime configuration. fs := flag.NewFlagSet(name, flag.ContinueOnError) fs.StringVar(&c.Addr, "addr", c.Addr, "the address on which to listen") fs.StringVar(&c.Base, "base", c.Base, "the base path for the server") fs.BoolVar(&c.Verbose, "v", c.Verbose, "be verbose") fs.DurationVar(&c.WriteTimeout, "wt", c.WriteTimeout, "the write timeout") err := fs.Parse(dic.MustParam("args").([]string)) if err != nil { return nil, err } vs := envflag.NewVarSet(fs) vs.SetPrefix(name) vs.Parse() return c, nil } func statsService(dic *izidic.Container) (any, error) { return web.NewStats(), nil } func webService(dic *izidic.Container) (any, error) { wd := container{dic} return &web.Server{ BaseContext: context.Background(), Config: wd.Config(), Logger: wd.Logger(), Stats: wd.Stats(), }, nil } // resolve configures dependency injection. func resolve(w io.Writer, args []string) *izidic.Container { dic := izidic.New() dic.Register("config", configService) dic.Register("logger", loggerService) dic.Register("stats", statsService) dic.Register("web", webService) dic.Store("name", "fizzbuzz") dic.Store("args", args[1:]) dic.Store("writer", w) dic.Freeze() return dic }