AdGuardHome/internal/home/options.go

493 lines
14 KiB
Go
Raw Permalink Normal View History

package home
import (
"fmt"
2022-11-02 13:18:02 +00:00
"net/netip"
"os"
"strconv"
2022-11-02 13:18:02 +00:00
"strings"
2024-01-30 15:43:51 +00:00
"github.com/AdguardTeam/AdGuardHome/internal/configmigrate"
"github.com/AdguardTeam/AdGuardHome/internal/version"
"github.com/AdguardTeam/golibs/log"
2022-11-02 13:18:02 +00:00
"github.com/AdguardTeam/golibs/stringutil"
)
2022-11-02 13:18:02 +00:00
// TODO(a.garipov): Replace with package flag.
// options represents the command-line options.
type options struct {
2022-11-02 13:18:02 +00:00
// confFilename is the path to the configuration file.
confFilename string
2022-11-02 13:18:02 +00:00
// workDir is the path to the working directory where AdGuard Home stores
// filter data, the query log, and other data.
workDir string
2022-11-02 13:18:02 +00:00
// logFile is the path to the log file. If empty, AdGuard Home writes to
// stdout; if "syslog", to syslog.
logFile string
2022-11-02 13:18:02 +00:00
// pidFile is the file name for the file to which the PID is saved.
pidFile string
2022-11-02 13:18:02 +00:00
// serviceControlAction is the service action to perform. See
// [service.ControlAction] and [handleServiceControlAction].
serviceControlAction string
2022-11-02 13:18:02 +00:00
// bindHost is the address on which to serve the HTTP UI.
2023-07-03 12:10:40 +01:00
//
// Deprecated: Use bindAddr.
2022-11-02 13:18:02 +00:00
bindHost netip.Addr
2022-11-02 13:18:02 +00:00
// bindPort is the port on which to serve the HTTP UI.
2023-07-03 12:10:40 +01:00
//
// Deprecated: Use bindAddr.
2023-10-11 15:31:41 +01:00
bindPort uint16
2023-07-03 12:10:40 +01:00
// bindAddr is the address to serve the web UI on.
bindAddr netip.AddrPort
2022-11-02 13:18:02 +00:00
// checkConfig is true if the current invocation is only required to check
// the configuration file and exit.
checkConfig bool
2022-11-02 13:18:02 +00:00
// disableUpdate, if set, makes AdGuard Home not check for updates.
disableUpdate bool
2022-11-23 13:52:05 +00:00
// performUpdate, if set, updates AdGuard Home without GUI and exits.
performUpdate bool
2022-11-02 13:18:02 +00:00
// verbose shows if verbose logging is enabled.
verbose bool
2022-11-02 13:18:02 +00:00
// runningAsService flag is set to true when options are passed from the
// service runner
//
// TODO(a.garipov): Perhaps this could be determined by a non-empty
// serviceControlAction?
runningAsService bool
Pull request: 2508 ip conversion vol.2 Merge in DNS/adguard-home from 2508-ip-conversion-vol2 to master Closes #2508. Squashed commit of the following: commit 5b9d33f9cd352756831f63e34c4aea48674628c1 Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:15:17 2021 +0300 util: replace net.IPNet with pointer commit 680126de7d59464077f9edf1bbaa925dd3fcee19 Merge: d3ba6a6c 5a50efad Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:02:41 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit d3ba6a6cdd01c0aa736418fdb86ed40120169fe9 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 18:29:54 2021 +0300 all: remove last conversion commit 88b63f11a6c3f8705d7fa0c448c50dd646cc9214 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 14:12:45 2021 +0300 all: improve code quality commit 71af60c70a0dbaf55e2221023d6d2e4993c9e9a7 Merge: 98af3784 9f75725d Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 17:13:27 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit 98af3784ce44d0993d171653c13d6e83bb8d1e6a Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:32:53 2021 +0300 all: log changes commit e99595a172bae1e844019d344544be84ddd65e4e Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:06:49 2021 +0300 all: fix or remove remaining net.IP <-> string conversions commit 7fd0634ce945f7e4c9b856684c5199f8a84a543e Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jan 15 15:36:17 2021 +0300 all: remove redundant net.IP <-> string converions commit 5df8af030421237d41b67ed659f83526cc258199 Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:35:25 2021 +0300 stats: remove redundant net.IP <-> string conversion commit fbe4e3fc015e6898063543a90c04401d76dbb18f Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:20:35 2021 +0300 querylog: remove redundant net.IP <-> string conversion
2021-01-20 14:27:53 +00:00
2022-11-02 13:18:02 +00:00
// glinetMode shows if the GL-Inet compatibility mode is enabled.
glinetMode bool
Pull request: 2508 ip conversion vol.2 Merge in DNS/adguard-home from 2508-ip-conversion-vol2 to master Closes #2508. Squashed commit of the following: commit 5b9d33f9cd352756831f63e34c4aea48674628c1 Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:15:17 2021 +0300 util: replace net.IPNet with pointer commit 680126de7d59464077f9edf1bbaa925dd3fcee19 Merge: d3ba6a6c 5a50efad Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:02:41 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit d3ba6a6cdd01c0aa736418fdb86ed40120169fe9 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 18:29:54 2021 +0300 all: remove last conversion commit 88b63f11a6c3f8705d7fa0c448c50dd646cc9214 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 14:12:45 2021 +0300 all: improve code quality commit 71af60c70a0dbaf55e2221023d6d2e4993c9e9a7 Merge: 98af3784 9f75725d Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 17:13:27 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit 98af3784ce44d0993d171653c13d6e83bb8d1e6a Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:32:53 2021 +0300 all: log changes commit e99595a172bae1e844019d344544be84ddd65e4e Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:06:49 2021 +0300 all: fix or remove remaining net.IP <-> string conversions commit 7fd0634ce945f7e4c9b856684c5199f8a84a543e Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jan 15 15:36:17 2021 +0300 all: remove redundant net.IP <-> string converions commit 5df8af030421237d41b67ed659f83526cc258199 Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:35:25 2021 +0300 stats: remove redundant net.IP <-> string conversion commit fbe4e3fc015e6898063543a90c04401d76dbb18f Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:20:35 2021 +0300 querylog: remove redundant net.IP <-> string conversion
2021-01-20 14:27:53 +00:00
2022-11-02 13:18:02 +00:00
// noEtcHosts flag should be provided when /etc/hosts file shouldn't be
// used.
noEtcHosts bool
Pull request: 2508 ip conversion vol.2 Merge in DNS/adguard-home from 2508-ip-conversion-vol2 to master Closes #2508. Squashed commit of the following: commit 5b9d33f9cd352756831f63e34c4aea48674628c1 Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:15:17 2021 +0300 util: replace net.IPNet with pointer commit 680126de7d59464077f9edf1bbaa925dd3fcee19 Merge: d3ba6a6c 5a50efad Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:02:41 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit d3ba6a6cdd01c0aa736418fdb86ed40120169fe9 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 18:29:54 2021 +0300 all: remove last conversion commit 88b63f11a6c3f8705d7fa0c448c50dd646cc9214 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 14:12:45 2021 +0300 all: improve code quality commit 71af60c70a0dbaf55e2221023d6d2e4993c9e9a7 Merge: 98af3784 9f75725d Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 17:13:27 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit 98af3784ce44d0993d171653c13d6e83bb8d1e6a Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:32:53 2021 +0300 all: log changes commit e99595a172bae1e844019d344544be84ddd65e4e Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:06:49 2021 +0300 all: fix or remove remaining net.IP <-> string conversions commit 7fd0634ce945f7e4c9b856684c5199f8a84a543e Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jan 15 15:36:17 2021 +0300 all: remove redundant net.IP <-> string converions commit 5df8af030421237d41b67ed659f83526cc258199 Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:35:25 2021 +0300 stats: remove redundant net.IP <-> string conversion commit fbe4e3fc015e6898063543a90c04401d76dbb18f Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:20:35 2021 +0300 querylog: remove redundant net.IP <-> string conversion
2021-01-20 14:27:53 +00:00
2022-11-02 13:18:02 +00:00
// localFrontend forces AdGuard Home to use the frontend files from disk
// rather than the ones that have been compiled into the binary.
localFrontend bool
}
2022-11-02 13:18:02 +00:00
// initCmdLineOpts completes initialization of the global command-line option
// slice. It must only be called once.
func initCmdLineOpts() {
// The --help option cannot be put directly into cmdLineOpts, because that
// causes initialization cycle due to printHelp referencing cmdLineOpts.
cmdLineOpts = append(cmdLineOpts, cmdLineOpt{
updateWithValue: nil,
updateNoValue: nil,
effect: func(o options, exec string) (effect, error) {
return func() error { printHelp(exec); exitWithError(); return nil }, nil
},
serialize: func(o options) (val string, ok bool) { return "", false },
description: "Print this help.",
longName: "help",
shortName: "",
})
}
2022-11-02 13:18:02 +00:00
// effect is the type for functions used for their side-effects.
type effect func() (err error)
Pull request: 2508 ip conversion vol.2 Merge in DNS/adguard-home from 2508-ip-conversion-vol2 to master Closes #2508. Squashed commit of the following: commit 5b9d33f9cd352756831f63e34c4aea48674628c1 Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:15:17 2021 +0300 util: replace net.IPNet with pointer commit 680126de7d59464077f9edf1bbaa925dd3fcee19 Merge: d3ba6a6c 5a50efad Author: Eugene Burkov <e.burkov@adguard.com> Date: Wed Jan 20 17:02:41 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit d3ba6a6cdd01c0aa736418fdb86ed40120169fe9 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 18:29:54 2021 +0300 all: remove last conversion commit 88b63f11a6c3f8705d7fa0c448c50dd646cc9214 Author: Eugene Burkov <e.burkov@adguard.com> Date: Tue Jan 19 14:12:45 2021 +0300 all: improve code quality commit 71af60c70a0dbaf55e2221023d6d2e4993c9e9a7 Merge: 98af3784 9f75725d Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 17:13:27 2021 +0300 Merge branch 'master' into 2508-ip-conversion-vol2 commit 98af3784ce44d0993d171653c13d6e83bb8d1e6a Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:32:53 2021 +0300 all: log changes commit e99595a172bae1e844019d344544be84ddd65e4e Author: Eugene Burkov <e.burkov@adguard.com> Date: Mon Jan 18 16:06:49 2021 +0300 all: fix or remove remaining net.IP <-> string conversions commit 7fd0634ce945f7e4c9b856684c5199f8a84a543e Author: Eugene Burkov <e.burkov@adguard.com> Date: Fri Jan 15 15:36:17 2021 +0300 all: remove redundant net.IP <-> string converions commit 5df8af030421237d41b67ed659f83526cc258199 Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:35:25 2021 +0300 stats: remove redundant net.IP <-> string conversion commit fbe4e3fc015e6898063543a90c04401d76dbb18f Author: Eugene Burkov <e.burkov@adguard.com> Date: Thu Jan 14 16:20:35 2021 +0300 querylog: remove redundant net.IP <-> string conversion
2021-01-20 14:27:53 +00:00
2022-11-02 13:18:02 +00:00
// cmdLineOpt contains the data for a single command-line option. Only one of
// updateWithValue, updateNoValue, and effect must be present.
type cmdLineOpt struct {
updateWithValue func(o options, v string) (updated options, err error)
updateNoValue func(o options) (updated options, err error)
effect func(o options, exec string) (eff effect, err error)
2022-11-02 13:18:02 +00:00
// serialize is a function that encodes the option into a slice of
// command-line arguments, if necessary. If ok is false, this option should
// be skipped.
serialize func(o options) (val string, ok bool)
2022-11-02 13:18:02 +00:00
description string
longName string
shortName string
}
2022-11-02 13:18:02 +00:00
// cmdLineOpts are all command-line options of AdGuard Home.
var cmdLineOpts = []cmdLineOpt{{
updateWithValue: func(o options, v string) (options, error) {
o.confFilename = v
return o, nil
},
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
return o.confFilename, o.confFilename != ""
},
description: "Path to the config file.",
longName: "config",
shortName: "c",
}, {
updateWithValue: func(o options, v string) (options, error) { o.workDir = v; return o, nil },
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) { return o.workDir, o.workDir != "" },
description: "Path to the working directory.",
longName: "work-dir",
shortName: "w",
}, {
updateWithValue: func(o options, v string) (oo options, err error) {
o.bindHost, err = netip.ParseAddr(v)
2022-11-02 13:18:02 +00:00
return o, err
},
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
if !o.bindHost.IsValid() {
return "", false
}
2022-11-02 13:18:02 +00:00
return o.bindHost.String(), true
},
2023-07-03 12:10:40 +01:00
description: "Deprecated. Host address to bind HTTP server on. Use --web-addr. " +
"The short -h will work as --help in the future.",
longName: "host",
shortName: "h",
2022-11-02 13:18:02 +00:00
}, {
updateWithValue: func(o options, v string) (options, error) {
2023-10-11 15:31:41 +01:00
p, err := strconv.ParseUint(v, 10, 16)
if err != nil {
err = fmt.Errorf("parsing port: %w", err)
} else {
2023-10-11 15:31:41 +01:00
o.bindPort = uint16(p)
}
2022-11-02 13:18:02 +00:00
return o, err
2022-11-02 13:18:02 +00:00
},
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
if o.bindPort == 0 {
return "", false
}
2023-10-11 15:31:41 +01:00
return strconv.Itoa(int(o.bindPort)), true
2022-11-02 13:18:02 +00:00
},
2023-07-03 12:10:40 +01:00
description: "Deprecated. Port to serve HTTP pages on. Use --web-addr.",
2022-11-02 13:18:02 +00:00
longName: "port",
shortName: "p",
2023-07-03 12:10:40 +01:00
}, {
updateWithValue: func(o options, v string) (oo options, err error) {
o.bindAddr, err = netip.ParseAddrPort(v)
return o, err
},
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
return o.bindAddr.String(), o.bindAddr.IsValid()
},
description: "Address to serve the web UI on, in the host:port format.",
longName: "web-addr",
shortName: "",
2022-11-02 13:18:02 +00:00
}, {
updateWithValue: func(o options, v string) (options, error) {
o.serviceControlAction = v
return o, nil
},
2022-11-02 13:18:02 +00:00
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) {
return o.serviceControlAction, o.serviceControlAction != ""
},
description: `Service control action: status, install (as a service), ` +
`uninstall (as a service), start, stop, restart, reload (configuration).`,
longName: "service",
shortName: "s",
}, {
updateWithValue: func(o options, v string) (options, error) { o.logFile = v; return o, nil },
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) { return o.logFile, o.logFile != "" },
description: `Path to log file. If empty, write to stdout; ` +
`if "syslog", write to system log.`,
longName: "logfile",
shortName: "l",
}, {
updateWithValue: func(o options, v string) (options, error) { o.pidFile = v; return o, nil },
updateNoValue: nil,
effect: nil,
serialize: func(o options) (val string, ok bool) { return o.pidFile, o.pidFile != "" },
description: "Path to a file where PID is stored.",
longName: "pidfile",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.checkConfig = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.checkConfig },
description: "Check configuration and exit.",
longName: "check-config",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.disableUpdate = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.disableUpdate },
description: "Don't check for updates.",
longName: "no-check-update",
shortName: "",
2022-11-23 13:52:05 +00:00
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.performUpdate = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.performUpdate },
2023-01-19 12:04:46 +00:00
description: "Update the current binary and restart the service in case it's installed.",
2022-11-23 13:52:05 +00:00
longName: "update",
shortName: "",
2022-11-02 13:18:02 +00:00
}, {
updateWithValue: nil,
updateNoValue: nil,
2022-11-02 13:18:02 +00:00
effect: func(_ options, _ string) (f effect, err error) {
log.Info("warning: using --no-mem-optimization flag has no effect and is deprecated")
2022-11-02 13:18:02 +00:00
return nil, nil
},
2022-11-02 13:18:02 +00:00
serialize: func(o options) (val string, ok bool) { return "", false },
description: "Deprecated. Disable memory optimization.",
longName: "no-mem-optimization",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.noEtcHosts = true; return o, nil },
2022-06-02 15:55:48 +01:00
effect: func(_ options, _ string) (f effect, err error) {
log.Info(
2023-04-18 14:07:11 +01:00
"warning: --no-etc-hosts flag is deprecated " +
"and will be removed in the future versions; " +
2024-03-12 14:45:11 +00:00
"set clients.runtime_sources.hosts and dns.hostsfile_enabled " +
"in the configuration file to false instead",
2022-06-02 15:55:48 +01:00
)
return nil, nil
},
2024-03-12 14:45:11 +00:00
serialize: func(o options) (val string, ok bool) { return "", o.noEtcHosts },
description: "Deprecated: use clients.runtime_sources.hosts and dns.hostsfile_enabled " +
"instead. Do not use the OS-provided hosts.",
longName: "no-etc-hosts",
shortName: "",
2022-11-02 13:18:02 +00:00
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.localFrontend = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.localFrontend },
description: "Use local frontend directories.",
longName: "local-frontend",
shortName: "",
2022-11-02 13:18:02 +00:00
}, {
updateWithValue: nil,
2022-11-02 13:18:02 +00:00
updateNoValue: func(o options) (options, error) { o.verbose = true; return o, nil },
effect: nil,
2022-11-02 13:18:02 +00:00
serialize: func(o options) (val string, ok bool) { return "", o.verbose },
description: "Enable verbose output.",
longName: "verbose",
shortName: "v",
}, {
updateWithValue: nil,
updateNoValue: func(o options) (options, error) { o.glinetMode = true; return o, nil },
effect: nil,
serialize: func(o options) (val string, ok bool) { return "", o.glinetMode },
description: "Run in GL-Inet compatibility mode.",
longName: "glinet",
shortName: "",
}, {
updateWithValue: nil,
updateNoValue: nil,
effect: func(o options, exec string) (effect, error) {
return func() error {
if o.verbose {
2024-01-30 15:43:51 +00:00
fmt.Print(version.Verbose(configmigrate.LastSchemaVersion))
2022-11-02 13:18:02 +00:00
} else {
fmt.Println(version.Full())
}
2022-11-02 13:18:02 +00:00
os.Exit(0)
2022-11-02 13:18:02 +00:00
return nil
}, nil
},
serialize: func(o options) (val string, ok bool) { return "", false },
description: "Show the version and exit. Show more detailed version description with -v.",
longName: "version",
shortName: "",
}}
// printHelp prints the entire help message. It exits with an error code if
// there are any I/O errors.
func printHelp(exec string) {
b := &strings.Builder{}
stringutil.WriteToBuilder(
b,
"Usage:\n\n",
fmt.Sprintf("%s [options]\n\n", exec),
"Options:\n",
)
var err error
for _, opt := range cmdLineOpts {
val := ""
2022-11-02 13:18:02 +00:00
if opt.updateWithValue != nil {
val = " VALUE"
}
2022-11-02 13:18:02 +00:00
longDesc := opt.longName + val
if opt.shortName != "" {
_, err = fmt.Fprintf(b, " -%s, --%-28s %s\n", opt.shortName, longDesc, opt.description)
} else {
2022-11-02 13:18:02 +00:00
_, err = fmt.Fprintf(b, " --%-32s %s\n", longDesc, opt.description)
}
if err != nil {
2022-11-02 13:18:02 +00:00
// The only error here can be from incorrect Fprintf usage, which is
// a programmer error.
panic(err)
}
}
2022-11-02 13:18:02 +00:00
_, err = fmt.Print(b)
if err != nil {
// Exit immediately, since not being able to print out a help message
// essentially means that the I/O is very broken at the moment.
exitWithError()
}
}
2022-11-02 13:18:02 +00:00
// parseCmdOpts parses the command-line arguments into options and effects.
func parseCmdOpts(cmdName string, args []string) (o options, eff effect, err error) {
// Don't use range since the loop changes the loop variable.
argsLen := len(args)
for i := 0; i < len(args); i++ {
arg := args[i]
isKnown := false
for _, opt := range cmdLineOpts {
isKnown = argMatches(opt, arg)
if !isKnown {
continue
}
if opt.updateWithValue != nil {
i++
if i >= argsLen {
return o, eff, fmt.Errorf("got %s without argument", arg)
}
2022-11-02 13:18:02 +00:00
o, err = opt.updateWithValue(o, args[i])
} else {
o, eff, err = updateOptsNoValue(o, eff, opt, cmdName)
}
2022-11-02 13:18:02 +00:00
if err != nil {
return o, eff, fmt.Errorf("applying option %s: %w", arg, err)
}
break
}
2022-11-02 13:18:02 +00:00
if !isKnown {
return o, eff, fmt.Errorf("unknown option %s", arg)
}
}
return o, eff, err
}
// argMatches returns true if arg matches command-line option opt.
func argMatches(opt cmdLineOpt, arg string) (ok bool) {
if arg == "" || arg[0] != '-' {
return false
}
arg = arg[1:]
if arg == "" {
return false
}
return (opt.shortName != "" && arg == opt.shortName) ||
(arg[0] == '-' && arg[1:] == opt.longName)
}
// updateOptsNoValue sets values or effects from opt into o or prev.
func updateOptsNoValue(
o options,
prev effect,
opt cmdLineOpt,
cmdName string,
) (updated options, chained effect, err error) {
if opt.updateNoValue != nil {
o, err = opt.updateNoValue(o)
if err != nil {
return o, prev, err
}
2022-11-02 13:18:02 +00:00
return o, prev, nil
}
next, err := opt.effect(o, cmdName)
if err != nil {
return o, prev, err
}
2022-11-02 13:18:02 +00:00
chained = chainEffect(prev, next)
return o, chained, nil
}
2022-11-02 13:18:02 +00:00
// chainEffect chans the next effect after the prev one. If prev is nil, eff
// only calls next. If next is nil, eff is prev; if prev is nil, eff is next.
func chainEffect(prev, next effect) (eff effect) {
if prev == nil {
return next
} else if next == nil {
return prev
}
eff = func() (err error) {
err = prev()
if err != nil {
return err
}
return next()
}
2022-11-02 13:18:02 +00:00
return eff
}
2022-11-02 13:18:02 +00:00
// optsToArgs converts command line options into a list of arguments.
func optsToArgs(o options) (args []string) {
for _, opt := range cmdLineOpts {
val, ok := opt.serialize(o)
if !ok {
continue
}
if opt.shortName != "" {
args = append(args, "-"+opt.shortName)
} else {
args = append(args, "--"+opt.longName)
}
if val != "" {
args = append(args, val)
}
}
2022-11-02 13:18:02 +00:00
return args
}