Help us shape the future of Go! Take the 2026 Go Developer Survey

config

package
v0.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package config 提供 YAML 配置文件加载、struct tag 增强和反射合并。

所有 enjoydream 后端服务共享此包,统一配置加载方式。

核心特性

  • 泛型加载:LoadBootstrap[T] 自动反序列化到目标结构体
  • Struct tag 增强:default/env/range/options 零值填充、环境变量注入与 fail-closed 校验
  • 反射合并:OverlayMerge 支持嵌套结构体和指针字段的深度合并
  • Nacos 集成:配合 nacos.LoadAndMerge 实现远程配置覆盖本地默认值

快速接入

定义配置结构体:

type AppConfig struct {
    Port    int            `yaml:"port"`
    Nacos   nacos.Bootstrap `yaml:"nacos"`
    DB      struct {
        DSN string `yaml:"dsn"`
    } `yaml:"db"`
}

加载配置:

cfg, err := config.LoadBootstrap[AppConfig]("dev")
if err != nil {
    log.Fatal(err)
}

加载入口(按来源选)

  • `LoadBootstrap[T](envFlag)`:**etc/ 约定**下的路径查找(etc/config.yaml → etc/config-{env}.yaml),返回 *T;
  • `LoadFile(path, dst)`:**任意路径**(CLI -config 传的路径、容器内固定挂载位 如 /app/plugins.yaml、部署方自定路径);
  • `Unmarshal(data, dst)`:**字节源**(API 下发、模板渲染产物、测试夹具)。

三个入口共享**同一条解码线**(展开 ${VAR} → 可选严格判定 → 解码 → tag 接线), 因此严格模式(WithStrict())的诊断面(嵌套路径 + 该层合法键清单 + 近似拼写建议) **与来源无关**——不会因为「不是从 etc/ 约定路径读的」就退化成一条 “field X not found in type Y”。

Struct Tag 增强(default/env/range/options/require)

LoadBootstrap 在 yaml unmarshal 之后依次执行 ApplyEnv → FillDefaults → ValidateTags(经 ApplyTagPasses,带 FieldSet);四个函数也可单独用于任意 结构体指针:

type Server struct {
    Host    string        `default:"localhost"`
    Port    int           `default:"8080" env:"APP_PORT" range:"(0,65535]"`
    Timeout time.Duration `default:"5s" range:"(0,30s]"`
    Mode    string        `default:"dev" options:"dev,prod"`
    Secret  string        `require:"true" env:"APP_SECRET"` // 必须外部提供
}

优先级(写死):yaml 显式值 > env tag(env 已设置且字段为零值时才填)> default tag。

零值歧义与 FieldSet

0/""/false/空切片与"未设置"对反射不可区分。yaml 显式写 `port: 0` 时:

  • LoadBootstrap / ApplyTagPasses:内部构建 FieldSet,显式零值保留,不被 default / env 覆盖;
  • 单独调用 FillDefaults / ApplyEnv(不带 WithFieldSet):保持历史行为,零值 会被 default 覆盖——需要区分请传 FieldSetFromYAML + WithFieldSet,或对 少量字段用指针类型。

整份配置都要区分零值时,全量 *T 改造成本很高:优先用 FieldSet;少量关键 字段用指针类型仍可行。不要在未评估成本前把配置结构体整体指针化。

require

字段必须由 yaml / env 显式提供,default 不算「提供」(FillDefaults 永不填 require 字段)。带 FieldSet 时显式零值也算通过;ValidateTags 在缺失时报错 并指出应设置的 env 变量名。

env 卫生与命名检查

ValidateTags 只做**静态**检查(与进程环境无关):

  • env 变量名须匹配 `[A-Za-z_][A-Za-z0-9_]*`;
  • 同一次校验内重复的 env 名报错。

命名一致性是**可选**的:`LintEnvNaming` 返回「声明名 ≠ 推导约定名」 (`backup_archive_format` → `BACKUP_ARCHIVE_FORMAT`)的诊断列表;需要 fail-closed 时传 `WithEnvNamingLint()`。有意自定义名(如 `LOG_PATH`)请打 `ignorederived:"true"` 豁免。不要依赖「环境里恰好有某变量」做拼写判定—— 会误报 PATH/HOME 等系统变量,且漏掉无人设置的真拼写错误(core#8)。

env 命名策略(前缀 / 逐字段覆盖)

字段的 env 变量名默认由结构体作者逐字段写死(`env:"NAME"` tag)。宿主若要 **统一前缀**(`PB_` / `APP_` / `SVC_`)或**逐字段改名**(历史 env 名与 yaml 键对不上、需保留兼容),用 EnvNaming——见 envnaming.go:

cfg, err := config.LoadBootstrap[MyConfig]("dev",
    config.WithEnvPrefix("PB_"),                    // 全局前缀
    config.WithEnvOverrides(map[string]string{
        "upload.local_import.enabled": "PB_LOCAL_IMPORT_ENABLED", // 逐字段覆盖
    }),
)

名字解析顺序:覆盖表 → env tag → 路径派生名(yaml 键路径的 SCREAMING_SNAKE)。 覆盖表命中即用表里的**绝对名**(值为空串 = 该字段不走 env);前缀只加在 本包推导的名字上。**未启用策略时行为与历史逐字一致**:只有打了 env tag 的 字段读 env。

覆盖表的键是 **yaml 键路径**(与严格模式诊断路径同口径):嵌套用 `.` 连接、 切片元素不编号、`yaml:",inline"` 的子字段拍平到父级。

支持类型:标量(string/int*/uint*/float*/bool)、time.Duration(time.ParseDuration 文法)、[]string(default/env 逗号切分)。range 仅数值字段,文法 `[a,b]`/`(a,b)`/`[a,b)`/`(a,b]`/单端 `[a,)`/`(,b]`(Duration 字段边界用 duration 文法如 "(0,30s]");options 仅 string 字段,空串也算未命中枚举 (可选枚举请配 default)。tag 非法或值越界一律报错(fail-closed)并携带 字段路径(嵌套 `Server.Port`、切片 `Items[1].Qty`)。

遍历覆盖嵌套 struct、*struct(nil 按需分配)、[]struct;不遍历 map, 跳过非导出字段。

ConfigWatcher 不自动执行这些 pass(其 parser 由调用方提供);热更场景可在 parser 里自行调用 ApplyTagPasses / ApplyEnv / FillDefaults / ValidateTags。

严格模式(未知键拒载)

默认未知键被 yaml.v3 **静默丢弃**——对 200+ 字段的配置结构体, 「拼错一个键」会表现为「我配了但没生效」(静默走零值缺省), 排查要等到看日志、查文档、怀疑平台才定位到是自己拼错了。

传 WithStrict() 开启严格模式:未知键**直接拒载**,错误信息给出 **嵌套路径**、**该层合法键清单**与**近似拼写建议**:

cfg, err := config.LoadBootstrap[AppConfig]("dev", config.WithStrict())
// config: 未知配置键 "llm.base_ur"(是否想写 "base_url"?);该层合法键:
// api_key, base_url, timeout_ms

严格模式是**显式开启**的——它会让「之前一直能跑(因为键拼错被忽略)」的配置 开始报错,切换前需确认现有配置无误。

合法键判定与 yaml.v3 的字段语义**逐条对齐**(yaml tag 优先、无 tag 取小写 字段名、显式 `,inline` 拍平、内嵌未导出类型同样处理)——不一致会把合法键 误报成未知键。

配置文件约定

配置文件路径:etc/config-{env}.yaml env 由启动参数或环境变量决定,如 dev、prod。

Package dotenv 环境文件加载(.env 纪律单源):按候选路径顺序取第一个 存在的 .env,逐行解析 KEY=VALUE(# 注释行跳过、值两侧引号剥除),已存在的 环境变量不被覆盖(真实环境优先于文件)。收编自 argus internal/config LoadDotEnv(v0.32.0)——服务开发期「自动加载 lacus/.env」的通用引导语义。

Index

Constants

This section is empty.

Variables

View Source
var DefaultDotEnvPaths = []string{"./.env", "../.env", "../../.env", "../../../.env"}

DefaultDotEnvPaths 常见 .env 位置(服务根目录向上逐级:本地 go run 与 子目录测试二进制的工作目录差异)。

Functions

func ApplyEnv added in v0.5.0

func ApplyEnv(v any, opts ...PassOption) error

ApplyEnv 依据 env tag 把环境变量写入字段:env 已设置且字段当前为零值时才填, 已有值(yaml 显式设置)不覆盖。

优先级约定:yaml 显式值 > env tag > default tag——LoadBootstrap 先 ApplyEnv 后 FillDefaults,env 填过的非零字段不会再被 default 覆盖。

带 FieldSet 时,YAML 显式提供的字段(含显式零值)不会被 env 覆盖;env 成功 写入会补记进 FieldSet,避免后续 FillDefaults 用 default 冲掉 env 值。

env 设为空串(或纯空白)视同未设置;env 值无法按字段类型解析时 fail-closed 报错(错误信息携带字段路径与变量名)。支持类型与遍历范围同 FillDefaults。

v 必须是非 nil 的结构体指针,否则返回错误。

func ApplyEnvOverlay added in v0.8.0

func ApplyEnvOverlay[T any](dst *T, opts ...PassOption) error

ApplyEnvOverlay 把环境变量解析到独立的 overlay 并覆盖 dst(#2): 与 ApplyEnv(只填零值字段,「yaml 显式值优先」)语义相反——本函数实现 「env 优先于配置文件」的 12-factor 场景(Helm/docker-compose 靠 env 传配置)。

等价手写:

overlay := &T{}
_ = ApplyEnv(overlay)        // 空结构体上解 env → env 非零值进入 overlay
OverlayMerge(dst, overlay)   // overlay 的非零值压过 dst(文件值)

env 未设置的字段不影响 dst(overlay 对应字段为零值,OverlayMerge 跳过)。 注意:OverlayMerge 是「非零值才覆盖」,env 显式配置零值(如 APP_PORT=0) 无法把文件里的非零值清成零——零值歧义与 ApplyEnv 一致,见 tags.go 文档。

可传 WithEnvNaming 启用 env 命名策略(前缀 / 逐字段覆盖)——见 envnaming.go。

func ApplyTagPasses added in v0.9.0

func ApplyTagPasses(cfg any, yamlData []byte, opts ...PassOption) error

ApplyTagPasses 对任意结构体指针执行完整 tag 接线:先按 yamlData 构建 FieldSet,再 ApplyEnv → FillDefaults → ValidateTags。与 LoadBootstrap 内部 同一套语义;热更 / 手写 loader 可直接复用。

可传 WithEnvNaming 启用 env 命名策略(前缀 / 逐字段覆盖)。

func FillDefaults added in v0.5.0

func FillDefaults(v any, opts ...PassOption) error

FillDefaults 依据 default tag 为零值字段填充默认值,非零字段一律不动。

优先级约定(与 LoadBootstrap 内 ApplyEnv → FillDefaults 的接线顺序配合): yaml 显式值 > env tag > default tag。

零值歧义:字段当前的 0/""/false/空切片与"未设置"对反射不可区分。yaml 里 显式写 `port: 0` 时,**不带 FieldSet** 调用本函数会被 default 覆盖(历史行为); 需要保留显式零值请传入 WithFieldSet(LoadBootstrap / ApplyTagPasses 已默认接线), 或对该字段打 require tag(default 永不填充,必须由 yaml/env 提供)。 整份配置都要区分零值时,少量字段用指针类型仍可行;改造成本过高则用 FieldSet,而不是全量 *T。

带 require tag 的字段本函数永不填充(default 不算「提供」)。

支持类型:string、int*/uint*/float*、bool、time.Duration(time.ParseDuration 文法,如 "5s")、[]string(逗号切分,元素去首尾空格并丢弃空元素);标量值 解析前同样去除首尾空格。其他类型携带 default tag 视为配置错误并报错。

遍历范围:嵌套 struct、*struct(nil 时若子结构体携带目标 tag 则按需分配后 继续填充)、[]struct / []*struct(仅已存在的元素);不遍历 map,跳过非导出字段。

v 必须是非 nil 的结构体指针,否则返回错误。

func Get

func Get(key string) string

Get 读取环境变量,未设置时返回空字符串。

func GetBool

func GetBool(key string) (bool, bool)

GetBool 读取环境变量并解析为布尔值。 返回 (value, true) 表示成功,(false, false) 表示未设置或解析失败。

func GetInt

func GetInt(key string) (int, bool)

GetInt 读取环境变量并解析为整数。 返回 (value, true) 表示成功,(0, false) 表示未设置或解析失败。

func GetIntWithDefault

func GetIntWithDefault(key string, defaultValue int) int

GetIntWithDefault 读取环境变量并解析为整数,未设置或解析失败时返回默认值。

func GetStringWithDefault

func GetStringWithDefault(key, defaultValue string) string

GetStringWithDefault 读取环境变量,未设置或为空时返回默认值。

func LintEnvNaming added in v0.9.2

func LintEnvNaming(v any, opts ...PassOption) ([]string, error)

LintEnvNaming 静态扫描 env tag 与推导约定名不一致的字段,返回诊断行 (不读进程环境、不 fail-closed)。适合 CI/lint 脚本或启动前自检。 有意自定义名请打 `ignorederived:"true"`。

可传 WithEnvNaming 启用命名策略口径:覆盖表命中的字段豁免;设了前缀则按 **路径级**派生名比对(与运行时实际读的变量名同口径)。

func LoadBootstrap

func LoadBootstrap[T any](envFlag string, opts ...LoadOption) (*T, error)

LoadBootstrap reads a local YAML config file, expands ${VAR} and ${VAR:default} environment variable placeholders, and unmarshals it into the provided struct type T.

After unmarshal, the struct tag passes run in order ApplyEnv → FillDefaults → ValidateTags (precedence: explicit yaml value > env tag > default tag; see tags.go for the supported tags). Non-struct config types skip the tag passes.

⚠️ 该接线是「yaml 优先于 env」。12-factor(env 优先于配置文件)场景请勿直接 复用 LoadBootstrap 的语义——在 yaml 填充后调用 ApplyEnvOverlay(dst) (见 overlay.go),env 非零值会覆盖文件值。

Lookup order:

  1. etc/config.yaml — used in Docker images (renamed from config-{env}.yaml at build time)
  2. etc/config-{envFlag}.yaml — used in local development (envFlag = "dev", "local", "prod")

可选传 WithStrict() 开启严格模式:未知键直接拒载,错误信息带嵌套路径与 该层合法键清单(见 strict.go)。默认仍是历史行为(未知键静默忽略)。

**不遵循 etc/ 约定的宿主**请用 LoadFile(任意路径)或 Unmarshal(字节源)—— 本函数只负责 etc/ 约定下的路径查找,解码语义与它们**同源**(unmarshalInto)。

func LoadDotEnv

func LoadDotEnv(paths ...string)

LoadDotEnv 依次尝试候选路径,加载第一个存在的 .env 后返回(不叠加多文件)。 paths 为空时用 DefaultDotEnvPaths。解析失败的行跳过不致命;已设置的环境 变量不被文件值覆盖。无任何候选文件存在时静默返回(无 .env 是常态)。

func LoadFile added in v0.11.0

func LoadFile(path string, dst any, opts ...LoadOption) error

LoadFile 从**任意路径**读配置并解码——不带 etc/ 约定查找。

适用:CLI -config 任意路径、容器内镜像内固定挂载位(如 /app/plugins.yaml)、 部署方自定路径等**不遵循 etc/config.yaml 约定**的宿主。

严格模式(传 WithStrict())的错误面与 LoadBootstrap 同源。

func MustGet

func MustGet(key string) string

MustGet 读取环境变量,未设置时 panic。 空字符串被视为有效值(已设置但为空)。

func OverlayMerge

func OverlayMerge[T any](dst *T, src *T)

OverlayMerge 将 src 中非零值字段覆盖合并到 dst 中,支持嵌套结构体。

支持的 merge tag:

  • "overwrite": 整体替换(包括零值),适用于需要强制覆盖的字段
  • "bool_or": 布尔字段使用 OR 语义(dst || src),适用于 enable 标志
  • "zeroable": 零值视为有效值合并(而非跳过),适用于需要显式设置 0/""/false 的字段

func ParseDuration

func ParseDuration(value string, fallback time.Duration) time.Duration

ParseDuration 解析 duration 字符串,失败或为空时返回 fallback。 适用于 YAML 配置中以字符串存储的 duration 字段。

用法:

d := config.ParseDuration(cfg.WorkerPollInterval, time.Second)

func Unmarshal added in v0.11.0

func Unmarshal(data []byte, dst any, opts ...LoadOption) error

Unmarshal 解码 YAML 字节 → 结构体指针:展开 ${VAR} → 可选严格判定 → tag 接线 (ApplyEnv → FillDefaults → ValidateTags;非结构体类型跳过 tag 接线)。

这是**与路径无关**的入口:配置来自 API 下发、模板渲染产物、测试夹具时用它。

严格模式(传 WithStrict())的错误面与文件路径版**同源**——同一套 unmarshalConfig、同一套 *UnknownKeyError(嵌套路径 + 该层合法键清单 + 近似拼写建议),**不会因为来源不同而丢诊断**。

⚠️ 该接线是「yaml 优先于 env」。12-factor(env 优先于配置文件)场景请勿直接 复用此语义——在 yaml 填充后调用 ApplyEnvOverlay(dst)(见 overlay.go), env 非零值会覆盖文件值。

func ValidateTags added in v0.5.0

func ValidateTags(v any, opts ...PassOption) error

ValidateTags 依据 range / options tag 做 fail-closed 校验,只读不写; 同时执行 require 与 env tag 卫生检查。

  • range:仅数值字段(含 time.Duration,边界用 duration 文法如 "(0,30s]")。 文法:`[a,b]`/`(a,b)`/`[a,b)`/`(a,b]` 四种区间 + 单端开区间 `[a,)`/`(,b]` (开侧方括号方向不敏感),边界支持小数;tag 文法非法或值越界均报错, 错误信息携带字段路径(嵌套用 `.` 连接,切片元素用 `Items[1].Qty`)。
  • options:仅 string 字段的枚举校验;当前值(含空串)必须命中枚举之一, 可选枚举字段请配合 default tag 给默认值。
  • require:字段必须由 yaml / env 显式提供(default 不算)。带 FieldSet 时 以集合为准(显式零值算通过);不带 FieldSet 时退化为「最终值非零」。
  • env 卫生(静态):变量名须为 `[A-Za-z_][A-Za-z0-9_]*`;同一次校验内重复的 env 名报错。不读进程环境——与环境相关的检查会误报 PATH/HOME 等系统变量、 且漏掉无人设置的真拼写错误(core#8),见 LintEnvNaming / WithEnvNamingLint。

与 FillDefaults/ApplyEnv 配合使用时放在最后(LoadBootstrap 接线顺序 ApplyEnv → FillDefaults → ValidateTags),校验的是填充后的最终值。

v 必须是非 nil 的结构体指针,否则返回错误。

Types

type ConfigSource

type ConfigSource interface {
	// Read 读取配置内容。
	Read() ([]byte, error)

	// Watch 监听配置变化,变化时调用 onChange。
	// 应在 ctx 取消时停止监听。
	Watch(ctx context.Context, onChange func())
}

ConfigSource 是配置源接口。

func OverlaySource added in v0.7.0

func OverlaySource(srcs ...ConfigSource) ConfigSource

OverlaySource 把多个 ConfigSource 组合为一个:

  • Read:按参数顺序逐个读取并深合并(后者覆盖前者),返回合并后的 blob。 任一子源读取失败即返回该错误
  • Watch:任一子源变化即触发 onChange(扇入),ctx 取消传播到全部子源

合并语义为 JSON blob 级深合并(对象递归、标量/数组整体覆盖),与 OverlayMerge 的结构体级合并互补——多来源场景推荐 OverlaySource + ConfigWatcher[T] 组合实现多源热更新。 所有子源须产出同一格式(JSON)内容。

type ConfigWatcher

type ConfigWatcher[T any] struct {
	// contains filtered or unexported fields
}

ConfigWatcher 是配置热更新监听器。 支持监听配置文件变化并自动重载。

func NewConfigWatcher

func NewConfigWatcher[T any](source ConfigSource, parser func([]byte) (*T, error)) *ConfigWatcher[T]

NewConfigWatcher 创建配置监听器。

参数:

  • source: 配置源(如文件、Nacos——见 nacos.NewConfigSource)
  • parser: 配置解析函数(如 YAML 解析)

func (*ConfigWatcher[T]) Get

func (w *ConfigWatcher[T]) Get() *T

Get 获取当前配置。

func (*ConfigWatcher[T]) OnChange

func (w *ConfigWatcher[T]) OnChange(fn func(old, new *T))

OnChange 注册配置变更回调。

func (*ConfigWatcher[T]) Reload

func (w *ConfigWatcher[T]) Reload() error

Reload 手动触发配置重载。 Reload 手动触发配置重载。 Swap 与 onChange 回调整体串行化,保证回调按重载顺序投递; 注意 onChange 内不得再调用 Reload(同 goroutine 重入会死锁)。

func (*ConfigWatcher[T]) SetLogger

func (w *ConfigWatcher[T]) SetLogger(l *slog.Logger)

SetLogger 设置自定义日志记录器。 任意时刻可调:logger 经 atomic.Pointer 保护(2026-09-06 复审修复, 原实现无锁写,Start 后 Watch 回调 goroutine 并发读构成数据竞争)。

func (*ConfigWatcher[T]) Start

func (w *ConfigWatcher[T]) Start(ctx context.Context) error

Start 启动配置监听。 会先执行一次初始加载,然后监听配置变化。

type EnvNaming added in v0.12.0

type EnvNaming struct {
	// Prefix 全局前缀,加在 env tag 名与路径派生名之前。空串 = 不加前缀。
	// 例:"PB_" 使 yaml 键 `upload.local_import.enabled` 读
	// `PB_UPLOAD_LOCAL_IMPORT_ENABLED`。
	Prefix string
	// Overrides 逐字段覆盖:键为 yaml 键路径,值为该字段的 env 变量名。
	// 值为空串表示该字段**不走 env**。nil / 空 map = 无覆盖。
	Overrides map[string]string
}

EnvNaming 描述 **env 变量名**的命名策略:全局前缀 + 逐字段覆盖。

现状(本包 v0.11.0 之前):字段只有打了 `env:"NAME"` tag 才会读 env,名字由 结构体作者逐字段写死;「约定名」(由 yaml 键推导的 SCREAMING_SNAKE)只被 LintEnvNaming / WithEnvNamingLint 用来**检查**名字是否一致,并不能**决定** 实际读的变量名。

于是两类宿主接不上:

  • 用统一前缀的 12-factor 宿主(`PB_` / `APP_` / `SVC_`):每个字段都得手写 `env:"PB_XXX"`,且**无法**让「没打 env tag 的字段」也按约定参与 env 读取;
  • 历史 env 名与 yaml 键对不上、需保留兼容的宿主:只能在结构体 tag 里钉死, 结构体不是自己的就改不动。

EnvNaming 补这两条入口。**名字解析顺序(先命中先赢)**:

  1. **覆盖表**(Overrides,键为 yaml 键路径):命中即用表里的名字——**绝对名**, 不加前缀;值为空串表示**该字段不走 env**。
  2. **env tag**:字段显式声明了 `env:"NAME"` 就用它(加了前缀则前缀在前)。
  3. **路径派生名**:没有显式名时按 yaml 键路径推导 (`upload.local_import.enabled` → `UPLOAD_LOCAL_IMPORT_ENABLED`)。

前缀(Prefix)加在**本包自己推导的名字**上(第 2、3 步),覆盖表里的名字不受 影响。默认(既无前缀也无覆盖表)行为与历史**逐字一致**:只有打了 env tag 的 字段读 env。

覆盖表的键是 **yaml 键路径**(与严格模式 UnknownKeyError.Path 同口径,便于 对账):嵌套用 `.` 连接、切片元素不编号(`items.qty` 而非 `items[0].qty`)、 `yaml:",inline"` 的子字段拍平到父级、`yaml:"-"` 的字段及其子树不可寻址。

type FieldSet added in v0.9.0

type FieldSet map[string]bool

FieldSet 记录结构体字段是否被**显式提供**(YAML 里出现过对应 key,或 env tag 成功写入)。用于消除零值歧义:`port: 0` 与未写字段对反射不可区分,但前者 是调用方的显式选择,不应被 default 覆盖,也不应被 env 覆盖。

键为字段路径(`Server.Port`、`Items[0].Qty`),与 tag 校验错误信息同格式。 由 FieldSetFromYAML 构建,经 WithFieldSet 传入 FillDefaults / ApplyEnv / ValidateTags;ApplyEnv 成功写入时会在集合里补记该路径。

func FieldSetFromYAML added in v0.9.0

func FieldSetFromYAML(v any, data []byte) (FieldSet, error)

FieldSetFromYAML 解析 YAML data,标记 v 的类型里在 data 中**显式出现**的字段。 key 存在即算显式提供(含 `port: 0` / `name: ""` / `port: null`);key 缺失则 该字段(及其嵌套)不算提供。

v 必须是非 nil 的结构体指针;data 为空或注释-only 时返回空集合。 与 LoadBootstrap 同源使用时请传入 ${VAR} 展开后的同一份 data。

func (FieldSet) Has added in v0.9.0

func (s FieldSet) Has(path string) bool

Has 报告字段路径是否被显式提供。

type LoadOption added in v0.10.0

type LoadOption func(*loadOptions)

LoadOption 加载选项。

func WithEnvOverrides added in v0.12.0

func WithEnvOverrides(m map[string]string) LoadOption

WithEnvOverrides 逐字段指定 env 变量名——给「历史 env 名与 yaml 键对不上、 需保留兼容」的宿主用。

键为 **yaml 键路径**(与严格模式诊断路径同口径):嵌套用 `.` 连接、切片元素 不编号、`yaml:",inline"` 的子字段拍平到父级。值为空串表示**该字段不走 env** (显式豁免,压过 env tag 与前缀)。

WithEnvOverrides(map[string]string{
    "upload.local_import.enabled": "PB_LOCAL_IMPORT_ENABLED",
    "legacy.retries":              "", // 该字段不读 env
})

覆盖表**只影响命中的字段**:未命中的字段行为不变(仍按 env tag 读 env,没有 tag 就不读)。覆盖优先于 env tag——表里写了就是表里说了算。

覆盖表里若有不存在的 yaml 键路径,该条目不生效(不报错):同一张表可以跨多个 配置结构体共用。

func WithEnvPrefix added in v0.12.0

func WithEnvPrefix(prefix string) LoadOption

WithEnvPrefix 给 env 名加全局前缀——给「用统一前缀的 12-factor 宿主」用。

前缀加在**本包推导的名字**上(env tag 名与路径派生名),覆盖表里的名字是 绝对名不受影响。例:

type Cfg struct {
    Upload struct {
        LocalImport struct {
            Enabled bool `yaml:"enabled"`
        } `yaml:"local_import"`
    } `yaml:"upload"`
}
// WithEnvPrefix("PB_") 后:Enabled 读 PB_UPLOAD_LOCAL_IMPORT_ENABLED
cfg, err := config.LoadBootstrap[Cfg]("dev", config.WithEnvPrefix("PB_"))

前缀为空时行为与历史完全一致:**只有打了 env tag 的字段读 env**。加了前缀后 额外多一层能力——**没打 env tag 的字段也按路径派生名参与 env 读取**(如上例的 Enabled),这正是「前缀即约定」的含义:宿主不必逐字段写 tag。

与 WithEnvOverrides 叠加时:覆盖表命中的字段用表里的绝对名,其余字段按本 前缀规则。

func WithStrict added in v0.10.0

func WithStrict() LoadOption

WithStrict 严格模式:**未知键直接拒载**,不做静默忽略。

默认(不传本选项)保持历史行为——未知键被 yaml.v3 静默丢弃。严格模式是 显式开启的,因为它会让「之前一直能跑(因为键拼错被忽略)」的配置开始报错。

错误信息带**嵌套路径**与**该层合法键清单**,并给出近似拼写建议:

config: 未知配置键 "llm.base_ur"(是否想写 "base_url"?);该层合法键:
base_url, timeout_ms, api_key

type PassOption added in v0.9.0

type PassOption func(*passConfig)

PassOption 定制 FillDefaults / ApplyEnv / ValidateTags 的单次执行。

func WithEnvNaming added in v0.12.0

func WithEnvNaming(n EnvNaming) PassOption

WithEnvNaming 给单独调用 ApplyEnv / ValidateTags / ApplyEnvOverlay 的场景 传命名策略(LoadOption 层的 WithEnvPrefix / WithEnvOverrides 是它的语法糖)。

典型用法是 12-factor 的 env 优先接线——yaml 填完后再用 env 覆盖:

_ = config.ApplyEnvOverlay(&cfg, config.WithEnvNaming(config.EnvNaming{
    Prefix: "PB_",
}))

func WithEnvNamingLint added in v0.9.2

func WithEnvNamingLint() PassOption

WithEnvNamingLint 让 ValidateTags 对「声明的 env 名 ≠ 由字段/yaml key 推导的 约定名」报错(纯静态,不读进程环境)。有意使用自定义名时在字段上打 `ignorederived:"true"` 豁免。默认关闭——命名不一致并不一定是错误。

func WithFieldSet added in v0.9.0

func WithFieldSet(s FieldSet) PassOption

WithFieldSet 传入显式提供集合:命中的路径视为「已有调用方值」—— FillDefaults 不再填 default,ApplyEnv 不再覆盖(即使当前是零值)。 ApplyEnv 成功写入时会向该集合补记路径(map 引用语义,调用方可见)。

type UnknownKeyError added in v0.10.0

type UnknownKeyError struct {
	// Path 嵌套路径(YAML 键形式),如 "llm.base_ur";顶层键时只有键名。
	Path string
	// Key 未知键名。
	Key string
	// Legal 该层级的合法键清单(YAML 键形式,已排序)。
	Legal []string
	// Near 近似拼写建议(与 Legal 中某键编辑距离最小且足够近时才有值)。
	Near string
}

UnknownKeyError 未知配置键错误。

func (*UnknownKeyError) Error added in v0.10.0

func (e *UnknownKeyError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL