Read config into struct
Working with Store and struct
介绍
cmdr.Set() 返回的 Store 对象包含了 app 的配置数据。层次化的配置数据可以通过 dottedPath 来访问。
// get `app.logging.file` as a string
println(cmdr.Set().MustString("logging.file"))但有时候,将一颗子树抽出并映射到 struct 中可能是有用的。
cmdr.v2 通过 Store 对象的方法 To() / GetSectionFrom() 来实现这样的映射。其原型为:
To(path string, holder any, opts ...MOpt[T]) (err error)同时,cmdr.To(path, holder, opts...) 是更便捷的调用。使用方法如下:
func TestTo(t *testing.T) {
ctx := context.Background()
app := cmdr.Create("app", "v1", `author`, `desc`,
cli.WithArgs("test-app", "--debug"),
).OnAction(func(ctx context.Context, cmd cli.Cmd, args []string) (err error) {
b := cmdr.Store().MustBool("debug")
println("debug flag: ", b)
if !b {
t.Fail()
}
println(cmdr.Set().Dump())
type manS struct {
Dir string
Type int
}
type genS struct {
Manual manS
}
var v genS
err = cmdr.To("cmd.generate", &v)
if err != nil {
t.Fail()
}
if v.Manual.Type != 1 {
t.Fail()
}
return
}).
Build()
err := app.Run(ctx)
if err != nil {
t.Errorf("Error: %v", err)
}
}func TestStore_GetSectionFrom(t *testing.T) {
conf := newBasicStore()
conf.Set("app.logging.words", []any{"a", 1, false})
conf.Set("app.server.sites", -1)
t.Logf("\nPath\n%v\n", conf.Dump())
type loggingS struct {
File uint
Rotate uint64
Words []any
}
type serverS struct {
Start int
Sites int
}
type appS struct {
Debug bool
Dump int
Verbose bool
Logging loggingS
Server serverS
}
type cfgS struct {
App appS
}
var ss cfgS
err := conf.GetSectionFrom("", &ss)
t.Logf("cfgS: %v | err: %v", ss, err)
assertEqual(t, []any{"a", 1, false}, ss.App.Logging.Words)
assertEqual(t, -1, ss.App.Server.Sites)
if !reflect.DeepEqual(ss.App.Logging.Words, []any{"a", 1, false}) {
t.Fail()
}
err = conf.GetSectionFrom("nonexist", nil)
t.Log("nothing happened")
err = conf.GetSectionFrom("nonexist", &ss)
t.Logf("cfgS: %v | err: %v", ss, err)
if err != nil {
t.Fail()
}
assertTrue(t, ss.App.Server.Sites == -1)
}
func newBasicStore(opts ...Opt) \*storeS {
conf := New(opts...)
conf.Set("app.debug", false)
// t.Logf("\nPath\n%v\n", conf.dump())
conf.Set("app.verbose", true)
// t.Logf("\nPath\n%v\n", conf.dump())
conf.Set("app.dump", 3)
conf.Set("app.logging.file", "/tmp/1.log")
conf.Set("app.server.start", 5)
// conf.Set("app.logging.rotate", 6)
// conf.Set("app.logging.words", []string{"a", "1", "false"})
ss := conf.WithPrefix("app.logging")
ss.Set("rotate", 6)
ss.Set("words", []string{"a", "1", "false"})
return conf.(*storeS)
}以 Test 1 为例,一个标准的 cmdr app 带有内建的 generate 子命令,相应的配置项节选如下:
app.cmd. <B>
generate. <B>
manual. <B>
dir <L> app.cmd.generate.manual.dir =>
type <L> app.cmd.generate.manual.type => 1
doc.dir <L> app.cmd.generate.doc.dir =>
shell. <B>
Shell <L> app.cmd.generate.shell.Shell => auto
dir <L> app.cmd.generate.shell.dir =>
output <L> app.cmd.generate.shell.output =>
auto <L> app.cmd.generate.shell.auto => true
zsh <L> app.cmd.generate.shell.zsh => false
bash <L> app.cmd.generate.shell.bash => false
fi <B>
sh <L> app.cmd.generate.shell.fish => false
g <L> app.cmd.generate.shell.fig => false
powershell <L> app.cmd.generate.shell.powershell => false
elvish <L> app.cmd.generate.shell.elvish => false从 app.cmd.generate 抽出子树映射到 genS 结构当中将会提取 app.cmd.generate.manual.dir 和 app.cmd.generate.manual.type 配置项。
这也意味着 v.(genS).Manual.Type 必然获得值 1。
了解更多
What is Next?
How is this guide?
最后更新于