home/options: add options -> args serialization
This commit is contained in:
parent
03506df25d
commit
d3428ca46c
|
@ -40,6 +40,33 @@ type arg struct {
|
||||||
updateWithValue func(o options, v string) (options, error) // the mutator for arguments with parameters
|
updateWithValue func(o options, v string) (options, error) // the mutator for arguments with parameters
|
||||||
updateNoValue func(o options) (options, error) // the mutator for arguments without parameters
|
updateNoValue func(o options) (options, error) // the mutator for arguments without parameters
|
||||||
effect func(o options, exec string) (f effect, err error) // the side-effect closure generator
|
effect func(o options, exec string) (f effect, err error) // the side-effect closure generator
|
||||||
|
|
||||||
|
serialize func(o options) []string // the re-serialization function back to arguments (return nil for omit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// {type}SliceOrNil functions check their parameter of type {type}
|
||||||
|
// against its zero value and return nil if the parameter value is
|
||||||
|
// zero otherwise they return a string slice of the parameter
|
||||||
|
|
||||||
|
func stringSliceOrNil(s string) []string {
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{s}
|
||||||
|
}
|
||||||
|
|
||||||
|
func intSliceOrNil(i int) []string {
|
||||||
|
if i == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []string{strconv.Itoa(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolSliceOrNil(b bool) []string {
|
||||||
|
if b {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var args []arg
|
var args []arg
|
||||||
|
@ -50,18 +77,21 @@ var configArg = arg{
|
||||||
func(o options, v string) (options, error) { o.configFilename = v; return o, nil },
|
func(o options, v string) (options, error) { o.configFilename = v; return o, nil },
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.configFilename) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var workDirArg = arg{
|
var workDirArg = arg{
|
||||||
"Path to the working directory",
|
"Path to the working directory",
|
||||||
"work-dir", "w",
|
"work-dir", "w",
|
||||||
func(o options, v string) (options, error) { o.workDir = v; return o, nil }, nil, nil,
|
func(o options, v string) (options, error) { o.workDir = v; return o, nil }, nil, nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.workDir) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostArg = arg{
|
var hostArg = arg{
|
||||||
"Host address to bind HTTP server on",
|
"Host address to bind HTTP server on",
|
||||||
"host", "h",
|
"host", "h",
|
||||||
func(o options, v string) (options, error) { o.bindHost = v; return o, nil }, nil, nil,
|
func(o options, v string) (options, error) { o.bindHost = v; return o, nil }, nil, nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.bindHost) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var portArg = arg{
|
var portArg = arg{
|
||||||
|
@ -80,6 +110,7 @@ var portArg = arg{
|
||||||
}
|
}
|
||||||
return o, err
|
return o, err
|
||||||
}, nil, nil,
|
}, nil, nil,
|
||||||
|
func(o options) []string { return intSliceOrNil(o.bindPort) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var serviceArg = arg{
|
var serviceArg = arg{
|
||||||
|
@ -89,42 +120,49 @@ var serviceArg = arg{
|
||||||
o.serviceControlAction = v
|
o.serviceControlAction = v
|
||||||
return o, nil
|
return o, nil
|
||||||
}, nil, nil,
|
}, nil, nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.serviceControlAction) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var logfileArg = arg{
|
var logfileArg = arg{
|
||||||
"Path to log file. If empty: write to stdout; if 'syslog': write to system log",
|
"Path to log file. If empty: write to stdout; if 'syslog': write to system log",
|
||||||
"logfile", "l",
|
"logfile", "l",
|
||||||
func(o options, v string) (options, error) { o.logFile = v; return o, nil }, nil, nil,
|
func(o options, v string) (options, error) { o.logFile = v; return o, nil }, nil, nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.logFile) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var pidfileArg = arg{
|
var pidfileArg = arg{
|
||||||
"Path to a file where PID is stored",
|
"Path to a file where PID is stored",
|
||||||
"pidfile", "",
|
"pidfile", "",
|
||||||
func(o options, v string) (options, error) { o.pidFile = v; return o, nil }, nil, nil,
|
func(o options, v string) (options, error) { o.pidFile = v; return o, nil }, nil, nil,
|
||||||
|
func(o options) []string { return stringSliceOrNil(o.pidFile) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var checkConfigArg = arg{
|
var checkConfigArg = arg{
|
||||||
"Check configuration and exit",
|
"Check configuration and exit",
|
||||||
"check-config", "",
|
"check-config", "",
|
||||||
nil, func(o options) (options, error) { o.checkConfig = true; return o, nil }, nil,
|
nil, func(o options) (options, error) { o.checkConfig = true; return o, nil }, nil,
|
||||||
|
func(o options) []string { return boolSliceOrNil(o.checkConfig) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var noCheckUpdateArg = arg{
|
var noCheckUpdateArg = arg{
|
||||||
"Don't check for updates",
|
"Don't check for updates",
|
||||||
"no-check-update", "",
|
"no-check-update", "",
|
||||||
nil, func(o options) (options, error) { o.disableUpdate = true; return o, nil }, nil,
|
nil, func(o options) (options, error) { o.disableUpdate = true; return o, nil }, nil,
|
||||||
|
func(o options) []string { return boolSliceOrNil(o.disableUpdate) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var verboseArg = arg{
|
var verboseArg = arg{
|
||||||
"Enable verbose output",
|
"Enable verbose output",
|
||||||
"verbose", "v",
|
"verbose", "v",
|
||||||
nil, func(o options) (options, error) { o.verbose = true; return o, nil }, nil,
|
nil, func(o options) (options, error) { o.verbose = true; return o, nil }, nil,
|
||||||
|
func(o options) []string { return boolSliceOrNil(o.verbose) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var glinetArg = arg{
|
var glinetArg = arg{
|
||||||
"Run in GL-Inet compatibility mode",
|
"Run in GL-Inet compatibility mode",
|
||||||
"glinet", "",
|
"glinet", "",
|
||||||
nil, func(o options) (options, error) { o.glinetMode = true; return o, nil }, nil,
|
nil, func(o options) (options, error) { o.glinetMode = true; return o, nil }, nil,
|
||||||
|
func(o options) []string { return boolSliceOrNil(o.glinetMode) },
|
||||||
}
|
}
|
||||||
|
|
||||||
var versionArg = arg{
|
var versionArg = arg{
|
||||||
|
@ -133,6 +171,7 @@ var versionArg = arg{
|
||||||
nil, nil, func(o options, exec string) (effect, error) {
|
nil, nil, func(o options, exec string) (effect, error) {
|
||||||
return func() error { fmt.Println(version()); os.Exit(0); return nil }, nil
|
return func() error { fmt.Println(version()); os.Exit(0); return nil }, nil
|
||||||
},
|
},
|
||||||
|
func(o options) []string { return nil },
|
||||||
}
|
}
|
||||||
|
|
||||||
var helpArg = arg{
|
var helpArg = arg{
|
||||||
|
@ -141,6 +180,7 @@ var helpArg = arg{
|
||||||
nil, nil, func(o options, exec string) (effect, error) {
|
nil, nil, func(o options, exec string) (effect, error) {
|
||||||
return func() error { _ = printHelp(exec); os.Exit(64); return nil }, nil
|
return func() error { _ = printHelp(exec); os.Exit(64); return nil }, nil
|
||||||
},
|
},
|
||||||
|
func(o options) []string { return nil },
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -253,3 +293,21 @@ func parse(exec string, ss []string) (o options, f effect, err error) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shortestFlag(a arg) string {
|
||||||
|
if a.shortName != "" {
|
||||||
|
return "-" + a.shortName
|
||||||
|
}
|
||||||
|
return "--" + a.longName
|
||||||
|
}
|
||||||
|
|
||||||
|
func serialize(o options) []string {
|
||||||
|
ss := []string{}
|
||||||
|
for _, arg := range args {
|
||||||
|
s := arg.serialize(o)
|
||||||
|
if s != nil {
|
||||||
|
ss = append(ss, append([]string{shortestFlag(arg)}, s...)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
|
@ -169,3 +169,69 @@ func TestParseUnknown(t *testing.T) {
|
||||||
testParseErr(t, "unknown plus", "+x")
|
testParseErr(t, "unknown plus", "+x")
|
||||||
testParseErr(t, "unknown dash", "-")
|
testParseErr(t, "unknown dash", "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSerialize(t *testing.T, o options, ss ...string) {
|
||||||
|
result := serialize(o)
|
||||||
|
if len(result) != len(ss) {
|
||||||
|
t.Fatalf("expected %s but got %s", ss, result)
|
||||||
|
}
|
||||||
|
for i, r := range result {
|
||||||
|
if r != ss[i] {
|
||||||
|
t.Fatalf("expected %s but got %s", ss, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeEmpty(t *testing.T) {
|
||||||
|
testSerialize(t, options{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeConfigFilename(t *testing.T) {
|
||||||
|
testSerialize(t, options{configFilename: "path"}, "-c", "path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeWorkDir(t *testing.T) {
|
||||||
|
testSerialize(t, options{workDir: "path"}, "-w", "path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeBindHost(t *testing.T) {
|
||||||
|
testSerialize(t, options{bindHost: "addr"}, "-h", "addr")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeBindPort(t *testing.T) {
|
||||||
|
testSerialize(t, options{bindPort: 666}, "-p", "666")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeLogfile(t *testing.T) {
|
||||||
|
testSerialize(t, options{logFile: "path"}, "-l", "path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializePidfile(t *testing.T) {
|
||||||
|
testSerialize(t, options{pidFile: "path"}, "--pidfile", "path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeCheckConfig(t *testing.T) {
|
||||||
|
testSerialize(t, options{checkConfig: true}, "--check-config")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeDisableUpdate(t *testing.T) {
|
||||||
|
testSerialize(t, options{disableUpdate: true}, "--no-check-update")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeService(t *testing.T) {
|
||||||
|
testSerialize(t, options{serviceControlAction: "run"}, "-s", "run")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeGLInet(t *testing.T) {
|
||||||
|
testSerialize(t, options{glinetMode: true}, "--glinet")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeMultiple(t *testing.T) {
|
||||||
|
testSerialize(t, options{
|
||||||
|
serviceControlAction: "run",
|
||||||
|
configFilename: "config",
|
||||||
|
workDir: "work",
|
||||||
|
pidFile: "pid",
|
||||||
|
disableUpdate: true,
|
||||||
|
}, "-c", "config", "-w", "work", "-s", "run", "--pidfile", "pid", "--no-check-update")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue