Documentation
¶
Overview ¶
Package cli implements datavase's command-line surface.
App takes its dependencies as fields so every command can be exercised in tests without a terminal, a keychain or a database.
Index ¶
Constants ¶
const CheckTimeout = 15 * time.Second
CheckTimeout bounds how long `dv check` waits before giving up.
Variables ¶
This section is empty.
Functions ¶
func HandleInit ¶ added in v0.6.0
HandleInit reports whether args ask for the wizard, and with what exit code.
A free function for the same reason HandleVersion is: `dv init` is dispatched before the configuration is read, since the whole point of it is that there is not one yet, and that puts it beyond App — and beyond App's tests with it. The one command a new user types should not be the one nothing checks.
func HandleVersion ¶
HandleVersion answers a request for the version, reporting whether it did.
It is a free function rather than a command on App because it has to run before the configuration is read: someone checking which build they have should not first be told to write a config file. That also means the flag spellings never reach flag.Parse, which would reject them as undefined.
Types ¶
type App ¶
type App struct {
Config *config.Config
Secrets secret.Store
Out io.Writer
Err io.Writer
// ReadPassword prompts for a password without echoing it. It is a field
// so tests can supply input without a terminal.
ReadPassword func(prompt string) (string, error)
// Probe opens a connection and returns the server version. It is a field
// so the command can be tested without a database.
Probe func(ctx context.Context, ds *config.DataSource, password string) (string, error)
// OpenUI connects and runs the terminal interface. It is a field so the
// dispatch logic can be tested without starting a terminal.
OpenUI func(ctx context.Context, ds *config.DataSource, password string, cfg *config.Config, opt UIOptions) error
}
App holds everything the commands need.
type UIOptions ¶ added in v0.3.0
type UIOptions struct {
// WorkDir is the directory of SQL work to attach, from --dir. Empty means
// the session starts unattached.
WorkDir string
}
UIOptions are the choices that belong to one invocation rather than to the configuration file.
It is a struct rather than more parameters so that the next such choice does not change the signature every caller and every test has to spell out.
type Wizard ¶ added in v0.6.0
type Wizard struct {
// Path is where the configuration will be written. An existing file there
// is never touched.
Path string
Out io.Writer
// Ask reads one line, returning def when the answer is empty.
Ask func(prompt, def string) (string, error)
// Choose picks one of options, returning def when the answer is empty.
//
// A def of noDefault means there is nothing to take: the question has to be
// answered, and an empty answer is asked again rather than resolved.
Choose func(prompt string, options []string, def int) (int, error)
// ReadPassword reads without echoing.
ReadPassword func(prompt string) (string, error)
Secrets secret.Store
// Probe opens a connection and returns the server version. The wizard uses
// the same one `dv check` does, so a datasource it accepts is one that
// really answered.
Probe func(ctx context.Context, ds *config.DataSource, password string) (string, error)
}
Wizard writes the first configuration file by asking for what goes in it.
It exists because the alternative is a YAML example printed to stderr, which asks someone who has not run datavase yet to know what "env" changes and to get a file past a parser that rejects unknown keys. The questions can be answered without knowing any of that.
Its dependencies are fields for the same reason App's are: the whole flow — the retry after an unreachable server, the file that gets written — is then exercisable without a terminal, a keychain or a database.
func (*Wizard) Run ¶ added in v0.6.0
Run asks the questions and writes the file, reporting why it could not.
Every question can be the last one: Ctrl-D and a pipe that runs out arrive the same way, and "EOF" is the reader's word for it rather than anything the person who pressed the key would recognise. Translating it once here is why no individual question has to.