di.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "io"
  7. "log"
  8. "time"
  9. "code.osinet.fr/fgm/izidic"
  10. "github.com/dcowgill/envflag"
  11. "code.osinet.fr/fgm/lbc/internal"
  12. "code.osinet.fr/fgm/lbc/web"
  13. )
  14. // containers wraps an [izidic.Container] to provide typed services accessors,
  15. // obviating the need to type-assert calls to Service.
  16. type container struct {
  17. *izidic.Container
  18. }
  19. func (c *container) Config() *web.Config {
  20. return c.MustService("config").(*web.Config)
  21. }
  22. func (c *container) Logger() *log.Logger {
  23. return c.MustService("logger").(*log.Logger)
  24. }
  25. func (c *container) Stats() *web.Stats {
  26. return c.MustService("stats").(*web.Stats)
  27. }
  28. func (c *container) Web() *web.Server {
  29. return c.MustService("web").(*web.Server)
  30. }
  31. // 12-factor/XI: log to stdout.
  32. func loggerService(dic *izidic.Container) (any, error) {
  33. sw, err := dic.Param("writer")
  34. if err != nil {
  35. return nil, err
  36. }
  37. w, ok := sw.(io.Writer)
  38. if !ok {
  39. return nil, fmt.Errorf("incorrect type for service writer: %T", sw)
  40. }
  41. logger := log.New(w, "", log.LstdFlags)
  42. // Support code not able to use an injected logger
  43. log.SetOutput(w)
  44. return logger, nil
  45. }
  46. func configService(dic *izidic.Container) (any, error) {
  47. name := dic.MustParam("name").(string)
  48. // Provide fallback configuration.
  49. c := &web.Config{
  50. Config: internal.Config{
  51. Verbose: false,
  52. },
  53. Addr: ":8080",
  54. Base: "/",
  55. WriteTimeout: 100 * time.Millisecond,
  56. }
  57. // Build runtime configuration.
  58. fs := flag.NewFlagSet(name, flag.ContinueOnError)
  59. fs.StringVar(&c.Addr, "addr", c.Addr, "the address on which to listen")
  60. fs.StringVar(&c.Base, "base", c.Base, "the base path for the server")
  61. fs.BoolVar(&c.Verbose, "v", c.Verbose, "be verbose")
  62. fs.DurationVar(&c.WriteTimeout, "wt", c.WriteTimeout, "the write timeout")
  63. err := fs.Parse(dic.MustParam("args").([]string))
  64. if err != nil {
  65. return nil, err
  66. }
  67. vs := envflag.NewVarSet(fs)
  68. vs.SetPrefix(name)
  69. vs.Parse()
  70. return c, nil
  71. }
  72. func statsService(dic *izidic.Container) (any, error) {
  73. return web.NewStats(), nil
  74. }
  75. func webService(dic *izidic.Container) (any, error) {
  76. wd := container{dic}
  77. return &web.Server{
  78. BaseContext: context.Background(),
  79. Config: wd.Config(),
  80. Logger: wd.Logger(),
  81. Stats: wd.Stats(),
  82. }, nil
  83. }
  84. // resolve configures dependency injection.
  85. func resolve(w io.Writer, args []string) *izidic.Container {
  86. dic := izidic.New()
  87. dic.Register("config", configService)
  88. dic.Register("logger", loggerService)
  89. dic.Register("stats", statsService)
  90. dic.Register("web", webService)
  91. dic.Store("name", "fizzbuzz")
  92. dic.Store("args", args[1:])
  93. dic.Store("writer", w)
  94. dic.Freeze()
  95. return dic
  96. }