pax_global_header00006660000000000000000000000064130006023170014502gustar00rootroot0000000000000052 comment=33e53e2d08656ccad000531debbf2656a896b695 easyconfig-1.0.15/000077500000000000000000000000001300060231700137155ustar00rootroot00000000000000easyconfig-1.0.15/README.md000066400000000000000000000025431300060231700152000ustar00rootroot00000000000000easyconfig: Easy bindings for configurable ========================================== [![GoDoc](https://godoc.org/gopkg.in/hlandau/easyconfig.v1?status.svg)](https://godoc.org/gopkg.in/hlandau/easyconfig.v1) easyconfig provides utilities for use with [configurable](https://github.com/hlandau/configurable). It makes it easy to get started with configurable using familiar interfaces. Import as: `gopkg.in/hlandau/easyconfig.v1` Packages provided ----------------- The `cflag` package allows you to easily declare configuration items in a flag-like manner. The `cstruct` package allows you to automatically generate configuration items from an annotated structure. The `adaptflag` package adapts declared configuration items to flags and registers them with the standard flag package and the [pflag](https://github.com/ogier/pflag) package. You can also use it with any flag package you like if it implements a similar registration interface. The `easyconfig` package itself provides a simple struct-based configuration interface; see the documentation in the examples. Licence ------- © 2015 Hugo Landau MIT License [Licenced under the licence with SHA256 hash `fd80a26fbb3f644af1fa994134446702932968519797227e07a1368dea80f0bc`, a copy of which can be found here.](https://raw.githubusercontent.com/hlandau/acme/master/_doc/COPYING.MIT) easyconfig-1.0.15/adaptconf/000077500000000000000000000000001300060231700156545ustar00rootroot00000000000000easyconfig-1.0.15/adaptconf/adaptconf.go000066400000000000000000000057231300060231700201510ustar00rootroot00000000000000// Package adaptconf adapts registered configurables to configuration file // formats. package adaptconf import "os" import "fmt" import "strings" import "path/filepath" import "gopkg.in/hlandau/configurable.v1" import "gopkg.in/hlandau/easyconfig.v1/cflag" import "gopkg.in/hlandau/svcutils.v1/exepath" import "github.com/BurntSushi/toml" var confFlag = cflag.String(nil, "conf", "", "Configuration file path") var lastConfPath string func LastConfPath() string { return lastConfPath } func LoadPath(confFilePath string) error { var m map[string]interface{} _, err := toml.DecodeFile(confFilePath, &m) if err != nil { return err } lastConfPath = confFilePath configurable.Visit(func(c configurable.Configurable) error { applyChild(c, m) return nil }) return nil } func LoadPaths(paths []string) error { confPath := confFlag.Value() if confPath == "" { for _, path := range paths { path = expandPath(path) if !pathExists(path) { continue } confPath = path } } if confPath == "" { return nil } return LoadPath(confPath) } func Load(programName string) error { return LoadPaths([]string{ fmt.Sprintf("/etc/%s/%s.conf", programName, programName), fmt.Sprintf("/etc/%s.conf", programName), fmt.Sprintf("etc/%s.conf", programName), fmt.Sprintf("$BIN/%s.conf", programName), fmt.Sprintf("$BIN/../etc/%s/%s.conf", programName, programName), fmt.Sprintf("$BIN/../etc/%s.conf", programName), }) } func pathExists(path string) bool { _, err := os.Stat(path) if err == nil { return true } return false } func expandPath(path string) string { if !strings.HasPrefix(path, "$BIN/") { return path } return filepath.Join(filepath.Dir(exepath.Abs), path[5:]) } func apply(c configurable.Configurable, v interface{}) error { cch, ok := c.(interface { CfChildren() []configurable.Configurable }) if ok { children := cch.CfChildren() if len(children) > 0 { return applyChildren(children, v) } } csv, ok := c.(interface { CfSetValue(x interface{}) error }) if !ok { return nil } cprio, ok := c.(interface { CfSetPriority(priority configurable.Priority) CfGetPriority() configurable.Priority }) if ok { prio := cprio.CfGetPriority() if prio <= configurable.ConfigPriority { err := csv.CfSetValue(v) if err != nil { return nil } cprio.CfSetPriority(configurable.ConfigPriority) } return nil } else { return csv.CfSetValue(v) } } func applyChildren(chs []configurable.Configurable, v interface{}) error { vm, ok := v.(map[string]interface{}) if !ok { return nil } for _, ch := range chs { applyChild(ch, vm) } return nil } func applyChild(ch configurable.Configurable, vm map[string]interface{}) error { name, ok := name(ch) if !ok { return nil } vch, ok := vm[name] if !ok { return nil } return apply(ch, vch) } func name(c configurable.Configurable) (name string, ok bool) { v, ok := c.(interface { CfName() string }) if !ok { return } return v.CfName(), true } easyconfig-1.0.15/adaptenv/000077500000000000000000000000001300060231700155175ustar00rootroot00000000000000easyconfig-1.0.15/adaptenv/adaptenv.go000066400000000000000000000023101300060231700176440ustar00rootroot00000000000000// Package adaptenv configures registers configurables from environment variables. package adaptenv import "gopkg.in/hlandau/configurable.v1" import "os" // Loads values from environment variables into any configurables which expose // CfEnvVarName() string. Priorities are checked. func Adapt() { configurable.Visit(func(c configurable.Configurable) error { adaptRecursive(c) return nil }) } func adaptRecursive(c configurable.Configurable) { cc, ok := c.(interface { CfChildren() []configurable.Configurable }) if ok { for _, ch := range cc.CfChildren() { adaptRecursive(ch) } } adapt(c) } func adapt(c configurable.Configurable) { cenv, ok := c.(interface { CfEnvVarName() string CfSetValue(x interface{}) error }) if !ok { return } envVarName := cenv.CfEnvVarName() if envVarName == "" { return } v, ok := os.LookupEnv(envVarName) if !ok { return } cprio, ok := c.(interface { CfGetPriority() configurable.Priority CfSetPriority(priority configurable.Priority) }) if ok { if cprio.CfGetPriority() > configurable.EnvPriority { return } } err := cenv.CfSetValue(v) if err != nil { return } if ok { cprio.CfSetPriority(configurable.EnvPriority) } } easyconfig-1.0.15/adaptflag/000077500000000000000000000000001300060231700156405ustar00rootroot00000000000000easyconfig-1.0.15/adaptflag/adaptflag.go000066400000000000000000000117041300060231700201150ustar00rootroot00000000000000// Package adaptflag adapts registered configurables to common flag parsing // packages, thereby making configurables configurable from the command line. package adaptflag import "fmt" import "flag" import "github.com/ogier/pflag" import "gopkg.in/alecthomas/kingpin.v2" import "gopkg.in/hlandau/configurable.v1" import "strings" var shortFlags = map[string]rune{} var shortFlagsReverse = map[rune]string{} func MapShort(name string, s rune) { shortFlags[name] = s if _, ok := shortFlagsReverse[s]; ok { panic(fmt.Sprintf("short flag already mapped: %#v=%#v", name, s)) } shortFlagsReverse[s] = name } func name(c configurable.Configurable) (name string, ok bool) { v, ok := c.(interface { CfName() string }) if !ok { return } return v.CfName(), true } func usageSummaryLine(c configurable.Configurable) (s string, ok bool) { v, ok := c.(interface { CfUsageSummaryLine() string }) if !ok { return } return v.CfUsageSummaryLine(), true } func defaultValue(c configurable.Configurable) (dflt interface{}, ok bool) { v, ok := c.(interface { CfDefaultValue() interface{} }) if !ok { return nil, false } return v.CfDefaultValue(), true } var errNotSupported = fmt.Errorf("not supported") type value struct { c configurable.Configurable } // The flag package uses this to get the default value. func (v *value) String() string { dflt, ok := defaultValue(v.c) if !ok { return "[configurable]" } return fmt.Sprintf("%#v", dflt) } func (v *value) Set(s string) error { cs, ok := v.c.(interface { CfSetValue(v interface{}) error }) if !ok { return errNotSupported } cp, ok := v.c.(interface { CfGetPriority() configurable.Priority CfSetPriority(priority configurable.Priority) }) if !ok { return cs.CfSetValue(s) } if cp.CfGetPriority() <= configurable.FlagPriority { err := cs.CfSetValue(s) if err != nil { return err } cp.CfSetPriority(configurable.FlagPriority) } return nil } func (v *value) Get() interface{} { cg, ok := v.c.(interface { CfValue() interface{} }) if !ok { return nil // ... } return cg.CfValue() } func (v *value) IsBoolFlag() bool { _, ok := v.Get().(bool) return ok } var adapted = map[interface{}]struct{}{} func adapt(path []string, c configurable.Configurable, f AdaptFunc) error { _, ok := adapted[c] if ok { return nil } name, ok := name(c) if !ok { return errNotSupported } _, ok = c.(interface { CfSetValue(v interface{}) error }) if !ok { return errNotSupported } v := &value{c: c} usage, _ := usageSummaryLine(c) dfltv, ok := defaultValue(c) dfltstr := "" if ok { dfltstr = fmt.Sprintf("%v", dfltv) } f(Info{ Name: name, Usage: usage, Value: v, Path: path, DefaultValueString: dfltstr, }) adapted[c] = struct{}{} return nil } // Gathered information about a configurable. This information makes it easy to // call flag.Var-like functions. type Info struct { Name string Usage string Path []string Value Value DefaultValueString string } // Called repeatedly by AdoptWithFunc. Your implementation of this function // should register the Value with the details provided. It is especially // suitable for use with functions like flag.Var or packages which provide // similar interfaces. type AdaptFunc func(info Info) func recursiveAdapt(path []string, c configurable.Configurable, f AdaptFunc) error { adapt(path, c, f) cc, ok := c.(interface { CfChildren() []configurable.Configurable }) if ok { n, ok := name(c) if ok { p := make([]string, 0, len(path)+1) path = append(p, path...) path = append(p, n) } for _, ch := range cc.CfChildren() { recursiveAdapt(path, ch, f) } } return nil } // The interface which this package exposes to the flag packages it adapts to. type Value interface { String() string Set(x string) error } // Similar to Adapt, but allows you to register to the flag package of your // choice, so long as it implements an interface similar to the flag.Var // function. func AdaptWithFunc(f AdaptFunc) { configurable.Visit(func(c configurable.Configurable) error { return recursiveAdapt(nil, c, f) }) } // Adapt registers all registered configurables as flags with the flag and // ogier/pflag packages. Note that Adapt will not do anything with // Configurables which it has already adapted once, thus it is safe to call // this function multiple times. func Adapt() { AdaptWithFunc(func(info Info) { dpn := DottedPath(info.Path) if len(dpn) > 0 { dpn += "." } dpn += info.Name flag.Var(info.Value, dpn, info.Usage) pflag.Var(info.Value, dpn, info.Usage) fl := kingpin.Flag(dpn, info.Usage) if info.DefaultValueString != "" { fl = fl.PlaceHolder(info.DefaultValueString) } else { fl = fl.PlaceHolder("\"\"") } if r, ok := shortFlags[dpn]; ok { fl = fl.Short(byte(r)) } fl.SetValue(info.Value) }) } func DottedPath(path []string) string { return strings.Join(path, ".") } easyconfig-1.0.15/cflag/000077500000000000000000000000001300060231700147715ustar00rootroot00000000000000easyconfig-1.0.15/cflag/cflag.go000066400000000000000000000205031300060231700163740ustar00rootroot00000000000000// Package cflag provides a flag-like means of declaring configurables. // // The functions in this package which take a Registerable argument can // have that argument passed as non-nil, in which case the configurable // becomes a child of the configurable passed, or nil, in which case // the configurable is registered at the top level. // // You should call Value() to get the value of a flag configurable. package cflag import "fmt" import "strconv" import "regexp" import "strings" import "gopkg.in/hlandau/configurable.v1" // Group type Registerable interface { Register(configurable configurable.Configurable) } type noReg struct{} // Dummy Registerable implementation which does not do anything. // // Can be used to inhibit autoregistration. var NoReg noReg func (r *noReg) Register(configurable configurable.Configurable) { } func register(r Registerable, c configurable.Configurable) { if r == nil { configurable.Register(c) } else { r.Register(c) } } type Group struct { configurables []configurable.Configurable name string } func (ig *Group) CfName() string { return ig.name } func (ig *Group) CfChildren() []configurable.Configurable { return ig.configurables } func (ig *Group) String() string { return fmt.Sprintf("%s", ig.name) } // Register a child configurable to the group. func (ig *Group) Register(cfg configurable.Configurable) { ig.configurables = append(ig.configurables, cfg) } // Creates a flag group. A Group is itself a configurable and can hold multiple // flags. func NewGroup(reg Registerable, name string) *Group { ig := &Group{ name: name, } register(reg, ig) return ig } // String type StringFlag struct { name, curValue, summaryLine, defaultValue string curValuep *string priority configurable.Priority onChange []func(*StringFlag) } func (sf *StringFlag) String() string { return fmt.Sprintf("SimpleFlag(%s: %#v)", sf.name, *sf.curValuep) } func (sf *StringFlag) CfSetValue(v interface{}) error { defer sf.notify() vs, ok := v.(string) if !ok { return fmt.Errorf("value must be a string") } *sf.curValuep = vs return nil } func (sf *StringFlag) notify() { for _, f := range sf.onChange { f(sf) } } func (sf *StringFlag) CfValue() interface{} { return *sf.curValuep } func (sf *StringFlag) CfName() string { return sf.name } func (sf *StringFlag) CfUsageSummaryLine() string { return sf.summaryLine } func (sf *StringFlag) CfDefaultValue() interface{} { return sf.defaultValue } // Get the flag's current value. func (sf *StringFlag) Value() string { return *sf.curValuep } // Set the flag's current value. func (sf *StringFlag) SetValue(value string) { *sf.curValuep = value } func (sf *StringFlag) RegisterOnChange(f func(*StringFlag)) { sf.onChange = append(sf.onChange, f) } func (sf *StringFlag) CfSetPriority(priority configurable.Priority) { sf.priority = priority } func (sf *StringFlag) CfGetPriority() configurable.Priority { return sf.priority } // Creates a flag of type string. The variable referenced by pointer v is used as // the storage location for the value of the configurable. func StringVar(reg Registerable, v *string, name, defaultValue, summaryLine string) *StringFlag { sf := &StringFlag{ name: name, summaryLine: summaryLine, defaultValue: defaultValue, curValue: defaultValue, curValuep: v, } if sf.curValuep == nil { sf.curValuep = &sf.curValue } register(reg, sf) return sf } // Creates a flag of type string. // // reg: See package-level documentation. // // summaryLine: One-line usage summary. func String(reg Registerable, name, defaultValue, summaryLine string) *StringFlag { return StringVar(reg, nil, name, defaultValue, summaryLine) } // Int type IntFlag struct { name, summaryLine string curValue, defaultValue int curValuep *int priority configurable.Priority onChange []func(*IntFlag) } func (sf *IntFlag) String() string { return fmt.Sprintf("IntFlag(%s: %#v)", sf.name, *sf.curValuep) } func (sf *IntFlag) CfSetValue(v interface{}) error { defer sf.notify() vi, ok := v.(int) if ok { *sf.curValuep = vi return nil } vs, ok := v.(string) if ok { vs = strings.TrimSpace(vs) n, err := strconv.ParseInt(vs, 0, 32) if err != nil { return err } *sf.curValuep = int(n) return nil } return fmt.Errorf("invalid value for configurable %#v, expecting int: %v", sf.name, v) } func (sf *IntFlag) notify() { for _, f := range sf.onChange { f(sf) } } func (sf *IntFlag) CfValue() interface{} { return sf.curValue } func (sf *IntFlag) CfName() string { return sf.name } func (sf *IntFlag) CfUsageSummaryLine() string { return sf.summaryLine } func (sf *IntFlag) CfDefaultValue() interface{} { return sf.defaultValue } // Get the flag's current value. func (sf *IntFlag) Value() int { return *sf.curValuep } // Set the flag's current value. func (sf *IntFlag) SetValue(value int) { *sf.curValuep = value } func (sf *IntFlag) RegisterOnChange(f func(*IntFlag)) { sf.onChange = append(sf.onChange, f) } func (sf *IntFlag) CfSetPriority(priority configurable.Priority) { sf.priority = priority } func (sf *IntFlag) CfGetPriority() configurable.Priority { return sf.priority } // Creates a flag of type int. The variable referenced by pointer v is used as // the storage location for the value of the configurable. func IntVar(reg Registerable, v *int, name string, defaultValue int, summaryLine string) *IntFlag { sf := &IntFlag{ name: name, summaryLine: summaryLine, defaultValue: defaultValue, curValue: defaultValue, curValuep: v, } if sf.curValuep == nil { sf.curValuep = &sf.curValue } register(reg, sf) return sf } // Creates a flag of type int. // // reg: See package-level documentation. // // summaryLine: One-line usage summary. func Int(reg Registerable, name string, defaultValue int, summaryLine string) *IntFlag { return IntVar(reg, nil, name, defaultValue, summaryLine) } // Bool type BoolFlag struct { name, summaryLine string curValue, defaultValue bool curValuep *bool priority configurable.Priority onChange []func(*BoolFlag) } func (sf *BoolFlag) String() string { return fmt.Sprintf("BoolFlag(%s: %#v)", sf.name, sf.curValue) } var re_no = regexp.MustCompilePOSIX(`^(00?|no?|f(alse)?)$`) func (sf *BoolFlag) CfSetValue(v interface{}) error { defer sf.notify() vb, ok := v.(bool) if ok { *sf.curValuep = vb return nil } vi, ok := v.(int) if ok { *sf.curValuep = (vi != 0) return nil } vs, ok := v.(string) if ok { vs = strings.TrimSpace(vs) *sf.curValuep = !re_no.MatchString(vs) return nil } return fmt.Errorf("invalid value for configurable %#v, expecting bool: %v", sf.name, v) } func (sf *BoolFlag) notify() { for _, f := range sf.onChange { f(sf) } } func (sf *BoolFlag) CfValue() interface{} { return sf.curValue } func (sf *BoolFlag) CfName() string { return sf.name } func (sf *BoolFlag) CfUsageSummaryLine() string { return sf.summaryLine } func (sf *BoolFlag) CfDefaultValue() interface{} { return sf.defaultValue } // Call to get the flag's current value. func (sf *BoolFlag) Value() bool { return *sf.curValuep } // Set the flag's current value. func (sf *BoolFlag) SetValue(value bool) { *sf.curValuep = value } func (sf *BoolFlag) RegisterOnChange(f func(*BoolFlag)) { sf.onChange = append(sf.onChange, f) } func (sf *BoolFlag) CfSetPriority(priority configurable.Priority) { sf.priority = priority } func (sf *BoolFlag) CfGetPriority() configurable.Priority { return sf.priority } // Creates a flag of type bool. // // reg: See package-level documentation. // // summaryLine: One-line usage summary. func Bool(reg Registerable, name string, defaultValue bool, summaryLine string) *BoolFlag { return BoolVar(reg, nil, name, defaultValue, summaryLine) } // Creates a flag of type bool. The variable referenced by pointer v is used as // the storage location for the value of the configurable. func BoolVar(reg Registerable, v *bool, name string, defaultValue bool, summaryLine string) *BoolFlag { sf := &BoolFlag{ name: name, summaryLine: summaryLine, defaultValue: defaultValue, curValue: defaultValue, curValuep: v, } if sf.curValuep == nil { sf.curValuep = &sf.curValue } register(reg, sf) return sf } easyconfig-1.0.15/cflag/cflag_test.go000066400000000000000000000013321300060231700174320ustar00rootroot00000000000000package cflag_test import "gopkg.in/hlandau/easyconfig.v1/cflag" import "gopkg.in/hlandau/easyconfig.v1/adaptflag" import flag "github.com/ogier/pflag" import "fmt" func Example() { var ( g = cflag.NewGroup(nil, "Program Options") bindFlag = cflag.String(g, "bind", ":80", "Address to bind server to (e.g. :80)") fooFlag = cflag.String(g, "foo", "", "Some flag") barFlag = cflag.Int(g, "bar", 42, "Some other flag") doStuffFlag = cflag.Bool(g, "doStuff", false, "Do stuff?") ) adaptflag.Adapt() flag.Parse() fmt.Printf("Bind: %s\n", bindFlag.Value()) fmt.Printf("Foo: %s\n", fooFlag.Value()) fmt.Printf("Bar: %d\n", barFlag.Value()) fmt.Printf("Do Stuff: %v\n", doStuffFlag.Value()) } easyconfig-1.0.15/cstruct/000077500000000000000000000000001300060231700154045ustar00rootroot00000000000000easyconfig-1.0.15/cstruct/cstruct.go000066400000000000000000000124301300060231700174220ustar00rootroot00000000000000// Package cstruct allows for the automatic generation of configurables from an // annotated structure. // // To use cstruct, you call New or MustNew, passing a pointer to an instance of // an annotated structure type. // // The supported field types are string, int and bool. A field is only used if // it is public and has the `default` or `usage` tags specified on it, or both. // The name of the field will be used as the configurable name. // // The following tags can be placed on fields: // // default: The default value as a string. // usage: A one-line usage summary. // // Once you have created a cstruct Configurable group, you must register it // appropriately as you see fit, for example by calling configurable.Register. package cstruct import "fmt" import "reflect" import "strings" import "regexp" import "strconv" import "gopkg.in/hlandau/configurable.v1" type group struct { configurables []configurable.Configurable name string } func (g *group) CfChildren() []configurable.Configurable { return g.configurables } func (g *group) CfName() string { return g.name } type value struct { name, usageSummaryLine, envVarName string v reflect.Value defaultValue interface{} priority configurable.Priority } func (v *value) CfName() string { return v.name } func (v *value) String() string { return fmt.Sprintf("cstruct-value(%s)", v.CfName()) } func (v *value) CfGetValue() interface{} { return v.v.Interface() } func (v *value) CfSetValue(x interface{}) error { xv := reflect.ValueOf(x) if !xv.Type().AssignableTo(v.v.Type()) { // Ensure that []interface{} (from e.g. TOML) can be converted to []T for some T if xv.Type().Kind() == reflect.Slice && v.v.Type().Kind() == reflect.Slice { // Append every element for i := 0; i < xv.Len(); i++ { // TODO: string coercion elem := reflect.ValueOf(xv.Index(i).Interface()) if !elem.Type().AssignableTo(v.v.Type().Elem()) { return fmt.Errorf("slice element not assignable with that type: %v, %v", v.v, elem) } v.v.Set(reflect.Append(v.v, elem)) } } // Try string coercion err := coercingSet(v.v, xv) if err != nil { return err } } v.v.Set(xv) return nil } func coercingSet(field reflect.Value, newValue reflect.Value) error { if !newValue.Type().AssignableTo(field.Type()) { if newValue.Type().Kind() != reflect.String { return fmt.Errorf("not assignable with that type") } pv, err := parseString(newValue.String(), field.Type()) if err != nil { return err } newValue = reflect.ValueOf(pv) if !newValue.Type().AssignableTo(field.Type()) { return fmt.Errorf("still not assignable with type after string conversion") } } field.Set(newValue) return nil } func (v *value) CfDefaultValue() interface{} { return v.defaultValue } func (v *value) CfUsageSummaryLine() string { return v.usageSummaryLine } func (v *value) CfEnvVarName() string { return v.envVarName } func (v *value) CfGetPriority() configurable.Priority { return v.priority } func (v *value) CfSetPriority(priority configurable.Priority) { v.priority = priority } var re_no = regexp.MustCompilePOSIX(`^(00?|no?|f(alse)?)$`) func parseString(s string, t reflect.Type) (interface{}, error) { switch t.Kind() { case reflect.Int: n, err := strconv.ParseInt(s, 0, 32) if err != nil { return nil, err } return int(n), nil case reflect.Bool: on := (s != "" && !re_no.MatchString(s)) return on, nil default: return s, nil } } // Like New, but panics on failure. func MustNew(target interface{}, name string) (c configurable.Configurable) { c, err := New(target, name) if err != nil { panic(err) } return c } // Creates a new group Configurable, with children representing the fields. // // The Configurables set the values of the fields of the instance. func New(target interface{}, name string) (c configurable.Configurable, err error) { t := reflect.TypeOf(target) v := reflect.ValueOf(target) if t.Kind() == reflect.Ptr { t = t.Elem() v = reflect.Indirect(v) } if t.Kind() != reflect.Struct { err = fmt.Errorf("target interface is not a struct: %v", t) return } g := &group{ name: name, } numFields := t.NumField() for i := 0; i < numFields; i++ { field := t.Field(i) name := strings.ToLower(field.Name) usage := field.Tag.Get("usage") dflt := field.Tag.Get("default") envVarName := field.Tag.Get("env") if usage == "" && dflt == "" { continue } vf := v.FieldByIndex(field.Index) var dfltv interface{} dfltv, err = parseString(dflt, vf.Type()) if err != nil { err = fmt.Errorf("invalid default value: %#v", dflt) return } vv := &value{ v: vf, name: name, envVarName: envVarName, usageSummaryLine: usage, defaultValue: dfltv, } if !vf.CanSet() { err = fmt.Errorf("field not assignable") return } err = vv.CfSetValue(dfltv) if err != nil && dflt != "" { panic(fmt.Sprintf("cannot set default value on field: %v", err)) } g.configurables = append(g.configurables, vv) // Do the type check now /*switch field.Type.Kind() { case reflect.Int: case reflect.String: case reflect.Bool: default: err = fmt.Errorf("unsupported field type: %v", field.Type) return }*/ } return g, nil } easyconfig-1.0.15/cstruct/cstruct_test.go000066400000000000000000000013531300060231700204630ustar00rootroot00000000000000package cstruct_test import "gopkg.in/hlandau/configurable.v1" import "gopkg.in/hlandau/easyconfig.v1/cstruct" import "gopkg.in/hlandau/easyconfig.v1/adaptflag" import flag "github.com/ogier/pflag" import "fmt" func Example() { type Config struct { Bind string `usage:"Address to bind server to (e.g. :80)" default:":80"` Foo string `usage:"Some flag" default:""` Bar int `usage:"Some other flag" default:"42"` DoStuff bool `usage:"Do stuff?" default:"false"` } cfg := &Config{} configurable.Register(cstruct.MustNew(cfg, "test")) adaptflag.Adapt() flag.Parse() fmt.Printf("Bind: %s\n", cfg.Bind) fmt.Printf("Foo: %s\n", cfg.Foo) fmt.Printf("Bar: %d\n", cfg.Bar) fmt.Printf("Do Stuff: %v\n", cfg.DoStuff) } easyconfig-1.0.15/easyconfig.go000066400000000000000000000041321300060231700163730ustar00rootroot00000000000000package easyconfig // import "gopkg.in/hlandau/easyconfig.v1" import "os" import "fmt" import "gopkg.in/hlandau/svcutils.v1/exepath" import "gopkg.in/hlandau/configurable.v1" import "gopkg.in/hlandau/easyconfig.v1/cstruct" import "gopkg.in/hlandau/easyconfig.v1/adaptflag" import "gopkg.in/hlandau/easyconfig.v1/adaptconf" import "gopkg.in/hlandau/easyconfig.v1/adaptenv" import "flag" // Easy configurator. Set the ProgramName and call Parse, passing a pointer to // a structure you want to fill with program-specific configuration values. type Configurator struct { ProgramName string configFilePath string inited bool } func (cfg *Configurator) Init(tgt interface{}) { if cfg.inited { return } cfg.inited = true } // Parse configuration values. tgt should be a pointer to a structure to be // filled using cstruct. If nil, no structure is registered using cstruct. func (cfg *Configurator) Parse(tgt interface{}) error { if tgt != nil && cfg.ProgramName != "" { if exepath.ProgramNameSetter == "default" { exepath.ProgramName = cfg.ProgramName } configurable.Register(cstruct.MustNew(tgt, cfg.ProgramName)) } adaptflag.Adapt() adaptenv.Adapt() flag.Parse() if cfg.ProgramName != "" { err := adaptconf.Load(cfg.ProgramName) if err != nil { return err } } cfg.configFilePath = adaptconf.LastConfPath() return nil } // Like Parse, but exits with an error message if an error occurs. func (cfg *Configurator) ParseFatal(tgt interface{}) { err := cfg.Parse(tgt) if err != nil { fmt.Fprintf(os.Stderr, "Cannot load configuration file: %v\n", err) os.Exit(1) } } // After calling Parse successfully, returns the path to the configuration file used, if any. func (cfg *Configurator) ConfigFilePath() string { return cfg.configFilePath } // Like Configurator.Parse. cfg may be nil. func Parse(cfg *Configurator, tgt interface{}) error { if cfg == nil { cfg = &Configurator{} } return cfg.Parse(tgt) } // Like Configurator.ParseFatal. cfg may be nil. func ParseFatal(cfg *Configurator, tgt interface{}) { if cfg == nil { cfg = &Configurator{} } cfg.ParseFatal(tgt) } easyconfig-1.0.15/exampleapp/000077500000000000000000000000001300060231700160515ustar00rootroot00000000000000easyconfig-1.0.15/exampleapp/exampleapp.go000066400000000000000000000004751300060231700205420ustar00rootroot00000000000000package main import _ "gopkg.in/hlandau/easyconfig.v1/exampleapp/examplelib" import "gopkg.in/hlandau/easyconfig.v1/adaptflag" //import "flag" import flag "github.com/ogier/pflag" func main() { adaptflag.AdaptWithFunc(func(info adaptflag.Info) { flag.Var(info.Value, info.Name, info.Usage) }) flag.Parse() } easyconfig-1.0.15/exampleapp/examplelib/000077500000000000000000000000001300060231700201735ustar00rootroot00000000000000easyconfig-1.0.15/exampleapp/examplelib/examplelib.go000066400000000000000000000040651300060231700226510ustar00rootroot00000000000000package examplelib import "gopkg.in/hlandau/easyconfig.v1/cflag" var g = cflag.NewGroup(nil, "Server Options") var bindFlag = cflag.String(g, "bind", ":53", "Address to bind to (e.g. 0.0.0.0:53)") var publicKeyFlag = cflag.String(g, "publicKey", "", "Path to the DNSKEY KSK public key file") var privateKeyFlag = cflag.String(g, "privateKey", "", "Path to the KSK's corresponding private key file") var zonePublicKeyFlag = cflag.String(g, "zonePublicKey", "", "Path to the DNSKEY ZSK public key file; if one is not specified, a temporary one is generated on startup and used only for the duration of that process") var zonePrivateKeyFlag = cflag.String(g, "zonePrivateKey", "", "Path to the ZSK's corresponding private key file") var namecoinRPCUsernameFlag = cflag.String(g, "namecoinRPCUsername", "", "Namecoin RPC username") var namecoinRPCPasswordFlag = cflag.String(g, "namecoinRPCPassword", "", "Namecoin RPC password") var namecoinRPCAddressFlag = cflag.String(g, "namecoinRPCAddress", "localhost:8336", "Namecoin RPC server address") var cacheMaxEntriesFlag = cflag.Int(g, "cacheMaxEntries", 100, "Maximum name cache entries") var selfNameFlag = cflag.String(g, "selfName", "", "The FQDN of this nameserver; if empty, a psuedo-hostname is generated") var selfIPFlag = cflag.String(g, "selfIP", "127.127.127.127", "The canonical IP address for this service") var httpListenAddrFlag = cflag.String(g, "httpListenAddr", "", "Address for the webserver to listen at (default: disabled)") var canonicalSuffixFlag = cflag.String(g, "canonicalSuffix", "bit", "Suffix to advertise via HTTP") var canonicalNameserversFlag = cflag.String(g, "canonicalNameservers", "", "Comma-separated list of nameservers to use for NS records; if blank, selfName (or autogenerated psuedo-hostname) is used") var hostmasterFlag = cflag.String(g, "hostmaster", "", "Hostmaster e. mail address") var vanityIPs = cflag.String(g, "vanityIPs", "", "Comma separated list of IP addresses to place in A/AAAA records at the zone apex (default: don't add any records)") var doStuff = cflag.Bool(g, "doStuff", false, "Do stuff") easyconfig-1.0.15/exampleapp2/000077500000000000000000000000001300060231700161335ustar00rootroot00000000000000easyconfig-1.0.15/exampleapp2/exampleapp2.conf000066400000000000000000000000271300060231700212170ustar00rootroot00000000000000[example] bind=":5050" easyconfig-1.0.15/exampleapp2/exampleapp2.go000066400000000000000000000044231300060231700207030ustar00rootroot00000000000000package main import "gopkg.in/hlandau/configurable.v1" import "gopkg.in/hlandau/easyconfig.v1/cstruct" import "gopkg.in/hlandau/easyconfig.v1/adaptflag" import "gopkg.in/hlandau/easyconfig.v1/adaptconf" import flag "github.com/ogier/pflag" import "fmt" type Config struct { Bind string `default:":53" usage:"Address to bind to (e.g. 0.0.0.0:53)"` PublicKey string `default:"" usage:"Path to the DNSKEY KSK public key file"` PrivateKey string `default:"" usage:"Path to the KSK's corresponding private key file"` ZonePublicKey string `default:"" usage:"Path to the DNSKEY ZSK public key file; if one is not specified, a temporary one is generated on startup and used only for the duration of that process"` ZonePrivateKey string `default:"" usage:"Path to the ZSK's corresponding private key file"` NamecoinRPCUsername string `default:"" usage:"Namecoin RPC username"` NamecoinRPCPassword string `default:"" usage:"Namecoin RPC password"` NamecoinRPCAddress string `default:"localhost:8336" usage:"Namecoin RPC server address"` CacheMaxEntries int `default:"100" usage:"Maximum name cache entries"` SelfName string `default:"" usage:"The FQDN of this nameserver. If empty, a psuedo-hostname is generated."` SelfIP string `default:"127.127.127.127" usage:"The canonical IP address for this service"` HTTPListenAddr string `default:"" usage:"Address for webserver to listen at (default: disabled)"` CanonicalSuffix string `default:"bit" usage:"Suffix to advertise via HTTP"` CanonicalNameservers string `default:"" usage:"Comma-separated list of nameservers to use for NS records. If blank, SelfName (or autogenerated psuedo-hostname) is used."` canonicalNameservers []string Hostmaster string `default:"" usage:"Hostmaster e. mail address"` VanityIPs string `default:"" usage:"Comma separated list of IP addresses to place in A/AAAA records at the zone apex (default: don't add any records)"` } func main() { tgt := &Config{} c := cstruct.MustNew(tgt, "example") configurable.Register(c) adaptflag.AdaptWithFunc(func(info adaptflag.Info) { flag.Var(info.Value, info.Name, info.Usage) }) flag.Parse() err := adaptconf.Load("exampleapp2") if err != nil { fmt.Printf("%v\n", err) } fmt.Printf("%#v\n", tgt) } easyconfig-1.0.15/manual/000077500000000000000000000000001300060231700151725ustar00rootroot00000000000000easyconfig-1.0.15/manual/manual.go000066400000000000000000000035161300060231700170030ustar00rootroot00000000000000// Package manual allows you to easily set configurables programmatically. package manual import "gopkg.in/hlandau/configurable.v1" import "strings" import "fmt" func ByName(name string) configurable.Configurable { parts := strings.Split(name, ".") top := topByName(parts[0]) if top == nil { return nil } return byName(top, parts[1:]) } func byName(c configurable.Configurable, p []string) configurable.Configurable { if len(p) == 0 { return c } cc, ok := c.(interface { CfChildren() []configurable.Configurable }) if ok { for _, ch := range cc.CfChildren() { n, ok := getName(ch) if ok && n == p[0] { return byName(ch, p[1:]) } } } return nil } var errStop = fmt.Errorf("stop") var errNotSupported = fmt.Errorf("not supported") func topByName(name string) configurable.Configurable { var cc configurable.Configurable configurable.Visit(func(c configurable.Configurable) error { n, ok := getName(c) if ok && n == name { cc = c return errStop } return nil }) return cc } func Set(name string, value interface{}) error { c := ByName(name) if c == nil { return fmt.Errorf("configurable not found: %s", name) } return set(c, value) } func set(c configurable.Configurable, s interface{}) error { cs, ok := c.(interface { CfSetValue(v interface{}) error }) if !ok { return errNotSupported } cp, ok := c.(interface { CfGetPriority() configurable.Priority CfSetPriority(priority configurable.Priority) }) if !ok { return cs.CfSetValue(s) } if cp.CfGetPriority() <= configurable.FlagPriority { err := cs.CfSetValue(s) if err != nil { return err } cp.CfSetPriority(configurable.FlagPriority) } return nil } func getName(c configurable.Configurable) (name string, ok bool) { v, ok := c.(interface { CfName() string }) if !ok { return } return v.CfName(), true }