Sketch
This commit is contained in:
96
src/internal/config/environment.go
Normal file
96
src/internal/config/environment.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Универсальная функция получения значения из окружения с парсером.
|
||||
func GetEnvAs[T any](key string, defaultVal T, parse func(string) (T, error)) T {
|
||||
raw, ok := os.LookupEnv(key)
|
||||
if !ok || strings.TrimSpace(raw) == "" {
|
||||
return defaultVal
|
||||
}
|
||||
v, err := parse(raw)
|
||||
if err != nil {
|
||||
// одна строка лога достаточно — не дублируем
|
||||
slog.Warn(fmt.Sprintf("cannot parse env %q, using default", key),
|
||||
"key", key, "raw", raw, "default", defaultVal, "error", err)
|
||||
return defaultVal
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
/* ====== БАЗОВЫЕ АДАПТЕРЫ ====== */
|
||||
|
||||
func ParseString(s string) (string, error) { // просто возвращаем trimmed string
|
||||
return strings.TrimSpace(s), nil
|
||||
}
|
||||
|
||||
func ParseBool(s string) (bool, error) {
|
||||
return strconv.ParseBool(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
func ParseInt(s string) (int, error) {
|
||||
i64, err := strconv.ParseInt(strings.TrimSpace(s), 10, 0)
|
||||
return int(i64), err
|
||||
}
|
||||
|
||||
func ParseInt64(s string) (int64, error) {
|
||||
return strconv.ParseInt(strings.TrimSpace(s), 10, 64)
|
||||
}
|
||||
|
||||
func ParseUint(s string) (uint, error) {
|
||||
u64, err := strconv.ParseUint(strings.TrimSpace(s), 10, 0)
|
||||
return uint(u64), err
|
||||
}
|
||||
|
||||
func ParseFloat64(s string) (float64, error) {
|
||||
return strconv.ParseFloat(strings.TrimSpace(s), 64)
|
||||
}
|
||||
|
||||
func ParseDuration(s string) (time.Duration, error) {
|
||||
// поддерживает "150ms", "2s", "1m", "24h"
|
||||
return time.ParseDuration(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
func ParseTimeRFC3339(s string) (time.Time, error) {
|
||||
return time.Parse(time.RFC3339, strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
func ParseURL(s string) (*url.URL, error) {
|
||||
return url.Parse(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
/* ====== СПИСКИ (CSV/SEPARATOR) ====== */
|
||||
|
||||
// Универсальный адаптер для списков с произвольным парсером элемента.
|
||||
func MakeListParser[T any](sep string, itemParser func(string) (T, error)) func(string) ([]T, error) {
|
||||
return func(s string) ([]T, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
var zero []T
|
||||
return zero, nil
|
||||
}
|
||||
parts := strings.Split(s, sep)
|
||||
out := make([]T, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
v, err := itemParser(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse list item %q: %w", p, err)
|
||||
}
|
||||
out = append(out, v)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Частые случаи:
|
||||
var ParseCSVStrings = MakeListParser[string](",", ParseString)
|
||||
var ParseCSVInts = MakeListParser[int](",", ParseInt)
|
||||
var ParseCSVFloat64 = MakeListParser[float64](",", ParseFloat64)
|
||||
Reference in New Issue
Block a user