Documentation
¶
Overview ¶
package clic is short for CLI Config. It implements a framework to load/parse cli configuration from a file, environment or flags.
Usage:
- Register functions (Register and RegisterCallback) should be called in `func init()` in packages, or before calling Parse.
- Parse function should be called at the beginning of "main()", before calling functions in other packages.
- Parse function must not be called in `func init()`, because other sub-packages may not finish initialization at that time.
See examples for the usage.
Example ¶
package main import ( "context" "flag" "fmt" "log/slog" "os" "time" "github.com/googollee/clic" ) func main() { // structs type Database struct { Driver string `clic:"driver,sqlite3,the driver of the database [sqlite3,mysql,postgres]"` URL string `clic:"url,./database.sqlite,the url of the database"` } type Log struct { Level slog.Level `clic:"level,INFO,the level of the log [DEBUG,INFO,WARN,ERROR]"` } initLog := func(ctx context.Context, log *Log) error { fmt.Println("set log level:", log.Level) return nil } // args oldArgs := os.Args os.Args = []string{oldArgs[0], "-database.driver", "driver", "-database.url", "url", "sub_command"} defer func() { os.Args = oldArgs }() // main code ctx := context.Background() var db Database clic.Register("database", &db) clic.RegisterCallback("log", initLog) // config should be finished in a minute. configCtx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() clic.Parse(configCtx) fmt.Println("database:", db) fmt.Println("remain args:", flag.Args()) }
Output: set log level: INFO database: {driver url} remain args: [sub_command]
Example (EmbedStruct) ¶
package main import ( "context" "flag" "fmt" "log" "os" "github.com/googollee/clic" ) func main() { // prepare env for _, key := range []string{"DEMO_VALUE"} { if err := os.Setenv(key, "value_from_env"); err != nil { log.Fatal("set env error:", err) } } // code starts type Inner struct { Value string `clic:"value,default,the value in the inner struct"` } type Config struct { Value string `clic:"value,default,the value in the outer struct"` Inner Inner `clic:"inner"` } fset := flag.NewFlagSet("", flag.PanicOnError) set := clic.NewSet(fset) var cfg Config set.RegisterValue("demo", &cfg) ctx := context.Background() if err := set.Parse(ctx, []string{"-demo.inner.value", "value_from_flag"}); err != nil { log.Fatal("parse error:", err) } fmt.Println("Value:", cfg.Value) fmt.Println("Inner.Value:", cfg.Inner.Value) }
Output: Value: value_from_env Inner.Value: value_from_flag
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var CommandLine = NewSet(flag.CommandLine, DefaultSources...)
var DefaultSources = []source.Source{ source.Flag(source.FlagSplitter(".")), source.File(source.FilePathFlag("config"), source.FileFormat(source.JSON{})), source.Env(source.EnvSplitter("_")), }
Functions ¶
func Parse ¶
Parse parses configuration from DefaultSources and os.Args.
If any error happens during calling, "Parse()" prints that error on Stderr and calls os.Exit to exit with "125" code.
func Register ¶
Register registers a "Config" value with the "name" as the scope name. The value is filled after calling Parse function.
Example:
package main func main() { ctx := context.Background() var dbCfg database.Config clic.Register("database", &db) clic.Parse(ctx) db := database.New(dbCfg) }
func RegisterCallback ¶
RegisterCallback registers a callback function with the "name" as the scope name. The callback is called after calling Parse function.
Example:
package main type Log struct { Level slog.Level `clic:"level,INFO,the level of log"` } func initLogLevel(ctx context.Context, cfg *Log) { slog.SetLevel(cfg.Level) } func main() { ctx := context.Background() clic.RegisterCallback("log", initLogLevel) clic.Parse(ctx) }
func RegisterType ¶ added in v0.1.5
RegisterType registers a "Config" type with the "name" as the scope name and returns a getter function which returns a parsed Config value.
Example:
package main func main() { ctx := context.Background() loadCfg := clic.RegisterType[database.Config]() clic.Parse(ctx) db := database.New(loadCfg()) }
Types ¶
type Set ¶
type Set struct {
// contains filtered or unexported fields
}
Example ¶
package main import ( "context" "flag" "fmt" "log" "log/slog" "github.com/googollee/clic" "github.com/googollee/clic/source" ) func main() { // structs type Database struct { Driver string `clic:"driver,sqlite3,the driver of the database [sqlite3,mysql,postgres]"` URL string `clic:"url,./database.sqlite,the url of the database"` } type Log struct { Level slog.Level `clic:"level,INFO,the level of the log [DEBUG,INFO,WARN,ERROR]"` } initLog := func(ctx context.Context, log *Log) error { fmt.Println("set log level:", log.Level) return nil } // set with sources fset := flag.NewFlagSet("", flag.ExitOnError) set := clic.NewSet(fset, // The order means srouce priority, flag > config file > env source.Flag(source.FlagSplitter(".")), source.File(source.FilePathFlag("config"), source.FileFormat(source.JSON{})), source.Env(source.EnvSplitter("_")), ) // args args := []string{"-log.level", "WARN", "-database.driver", "driver", "-database.url", "url", "other_cmd"} // main code ctx := context.Background() var db Database set.RegisterValue("database", &db) set.RegisterCallback("log", initLog) if err := set.Parse(ctx, args); err != nil { log.Fatal("parse error:", err) } fmt.Println("database:", db) fmt.Println("remain args:", fset.Args()) }
Output: set log level: WARN database: {driver url} remain args: [other_cmd]
Example (ShowHelp) ¶
package main import ( "bytes" "context" "errors" "flag" "fmt" "log" "log/slog" "strings" "github.com/googollee/clic" "github.com/googollee/clic/source" ) func main() { // structs type Database struct { Driver string `clic:"driver,sqlite3,the driver of the database [sqlite3,mysql,postgres]"` URL string `clic:"url,./database.sqlite,the url of the database"` } type Log struct { Level slog.Level `clic:"level,INFO,the level of the log [DEBUG,INFO,WARN,ERROR]"` } initLog := func(ctx context.Context, log *Log) error { fmt.Println("set log level:", log.Level) return nil } // set with sources fset := flag.NewFlagSet("", flag.ContinueOnError) var helpOutput bytes.Buffer fset.SetOutput(&helpOutput) set := clic.NewSet(fset, // The order means srouce priority, flag > config file > env source.Flag(source.FlagSplitter(".")), source.File(source.FilePathFlag("config"), source.FileFormat(source.JSON{})), source.Env(source.EnvSplitter("_")), ) // args args := []string{"-h"} // main code ctx := context.Background() var db Database set.RegisterValue("database", &db) set.RegisterCallback("log", initLog) if err := set.Parse(ctx, args); !errors.Is(err, flag.ErrHelp) { log.Fatal("parse error:", err) } fmt.Println(strings.ReplaceAll(helpOutput.String(), "\t", " ")) }
Output: Usage: -config string the path of the config file -database.driver value the driver of the database [sqlite3,mysql,postgres] (default sqlite3) -database.url value the url of the database (default ./database.sqlite) -log.level value the level of the log [DEBUG,INFO,WARN,ERROR] (default INFO)
Example (SourcePriorities) ¶
package main import ( "context" "flag" "fmt" "log" "os" "github.com/googollee/clic" ) func main() { // prepare env for _, key := range []string{"DEMO_VALUE_FLAG", "DEMO_VALUE_ENV", "DEMO_VALUE_FILE"} { if err := os.Setenv(key, "value_from_env"); err != nil { log.Fatal("set env error:", err) } } // prepare config file cfgFile, err := os.CreateTemp("", "config_*.json") if err != nil { log.Fatal("create temp file error:", err) } defer os.Remove(cfgFile.Name()) if _, err := cfgFile.WriteString(`{ "demo": { "value_flag": "value_from_file", "value_file": "value_from_file" } }`); err != nil { log.Fatal("write temp file error:", err) } if err := cfgFile.Close(); err != nil { log.Fatal("close temp file error:", err) } // prepare args args := []string{"-config", cfgFile.Name(), "-demo.value_flag", "value_from_flag"} // code starts type Config struct { ValueFlag string `clic:"value_flag,default,a test value in flag"` ValueEnv string `clic:"value_env,default,a test value in env"` ValueFile string `clic:"value_file,default,a test value in config file"` ValueDefault string `clic:"value_default,default,a test value by default"` } var cfg Config fset := flag.NewFlagSet("", flag.PanicOnError) set := clic.NewSet(fset, clic.DefaultSources...) set.RegisterValue("demo", &cfg) ctx := context.Background() if err := set.Parse(ctx, args); err != nil { log.Fatal("parse error:", err) } fmt.Println("ValueFlag:", cfg.ValueFlag) fmt.Println("ValueEnv:", cfg.ValueEnv) fmt.Println("ValueFile:", cfg.ValueFile) fmt.Println("ValueDefault:", cfg.ValueDefault) }
Output: ValueFlag: value_from_flag ValueEnv: value_from_env ValueFile: value_from_file ValueDefault: default