package dotenv import ( "errors" "log/slog" "reflect" "strconv" "strings" ) // Assign env tag matching values from envMap func Assign[T any](c *T, envMap map[string]string) error { if c == nil { return errors.New("nil arg") } slog.Info("env map", "v", envMap) val := reflect.Indirect(reflect.ValueOf(c)) name := val.Type().Name() for i := range val.NumField() { f := val.Type().Field(i) tag := f.Tag.Get("env") if tag == "" { continue } var v string var ok bool if multiTag := strings.Split(tag, ","); len(multiTag) > 1 { // multi tags like env:"DB_URL,PG_DB_URL" for _, mt := range multiTag { if v, ok = envMap[strings.TrimSpace(mt)]; ok { break } } } else { v, ok = envMap[tag] } if !ok { continue } field := val.FieldByName(f.Name) if !field.IsValid() { continue } switch f.Type.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: if v, err := strconv.ParseInt(v, 10, 64); err != nil { return err } else { field.SetInt(v) } case reflect.Float32, reflect.Float64: if v, err := strconv.ParseFloat(v, 10); err != nil { return err } else { field.SetFloat(v) } case reflect.Bool: if strings.EqualFold(v, "true") { field.SetBool(true) } else { field.SetBool(false) } default: field.SetString(v) } if name == "" { slog.Info("override value from env", "field", f.Name) } else { slog.Info("override value from env", "type", name, "field", f.Name) } } return nil }