Documentation
¶
Overview ¶
Package connedit adds, tests, edits and removes the connections a user creates from inside dbc — dbc web's connection form and the TUI's — as opposed to the config file's, which are the file's to change.
It is the UI-free half of both forms. The web turns its answers into HTTP statuses (web/conns.go) and the TUI into lines in the form and the log (tui/connform.go); what counts as a valid form, how a DSN is built from fields, what is kept when an edit leaves a field empty, and the order in which the running config and the saved-connections file are changed live here once, so the two UIs cannot drift apart on any of it.
It sits above both config and db (config must not import db: db opens what config describes), which is why it is a package of its own rather than part of either.
Where a connection lives:
┌── config file ([[connection]]) ──┐
startup: cfg ◄──┤ ├── file's names win a clash
(any dbc) └── connections.toml ──────────────┘
▲
└── Editor.Add / Edit / Delete (dbc web, the TUI)
Every change goes config first, file second:
Add ──► cfg.AddConn ──── name taken? ──► Conflict
│
└► saved.Add ── fails? ───────► cfg.RemoveConn (undo), error
Edit ─► web-added? ─ no ─► Invalid ("edit the file")
│ FormDSN, Check, expand
│ reconnects (name, driver, DSN or TLS changed)?
│ └► inUse(name) refuses? ─► that refusal
├► cfg.ReplaceConn ─ clash / gone ─► Conflict / NotFound
├► saved.Update ── fails? ─► cfg.ReplaceConn back, error
└► reconnects? mgr.Drop(old name)
Delete ► web-added? ─ no ─► Invalid
inUse(name) refuses? ─► that refusal
cfg.RemoveConn, mgr.Drop, saved.Delete (a failure here is
reported, but the removal stands for this run)
The config is changed first because it is where a duplicate name is caught atomically — two windows (or a window and a TUI) saving one name race there, under the config's lock, not in the file. A file that then fails to write is undone in the config, so the two never disagree about what exists for longer than one call.
"In use" is the caller's to judge: dbc web counts the query tabs on a connection across every window, the TUI has one — its active connection. Either way a connection something is on cannot be renamed, given a new DSN, driver or TLS, or removed, since that would pull the pool out from under a session and whatever transaction it holds. Its ai_rows alone can change at any time: the assistant reads it afresh for every question.
Index ¶
- Constants
- type EditResult
- type Editor
- func (e *Editor) Add(f Form) (warns []string, err error)
- func (e *Editor) Delete(name string, inUse func(name string) error) (removed bool, err error)
- func (e *Editor) Edit(name string, f Form, inUse func(name string) error) (EditResult, error)
- func (e *Editor) FileRefusal(name, verb string) error
- func (e *Editor) FormDSN(f *Form, from string) error
- func (e *Editor) Test(ctx context.Context, f Form, from string) (TestResult, error)
- func (e *Editor) UnsavedWarning(what string) string
- type Error
- type Form
- type Kind
- type TestResult
Constants ¶
const MaxName = 64
MaxName bounds a connection name. It is shown in the sidebar, the topbar and every history row: a name that long is a paste gone wrong.
const TestTimeoutCap = 30 * time.Second
TestTimeoutCap bounds a test when connect_timeout is 0 ("no limit"): a real connect may then wait on the OS, but a button press should answer while the user is still looking at it.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type EditResult ¶
type EditResult struct {
Renamed bool
Reconnects bool // name, driver, DSN or TLS changed: the old pool was dropped
Warnings []string
}
EditResult says what an edit changed, for the caller's follow-ups: a rename moves what the UI keeps under the old name (saved tabs, schema picks, tab groups).
type Editor ¶
type Editor struct {
Cfg *config.Config
Saved *config.SavedStore
// Mgr's pool for a connection is dropped when the connection changes
// what it dials or goes away, so the next connect opens a fresh one
// from the new entry. Nil drops nothing (tests).
Mgr *db.Manager
// App names this program in messages: "dbc web", "dbc". A clash in the
// file means another such program added the name since this one read
// it ("added by another dbc web").
App string
}
Editor changes the connections a user added, in the running config and in the saved-connections file together.
func (*Editor) Add ¶
Add adds a connection to the running config and keeps it in the saved-connections file. It does not connect: each UI switches to it right after, where the connect's outcome shows the way every connect's does.
func (*Editor) Delete ¶
Delete forgets a connection added from inside dbc. The config file's are refused, as is one inUse (nil: nothing ever is) says something is on.
removed reports whether the running config lost it: a file that then fails to write is an error, but the connection stays gone for this run — putting it back would only hand the user a connection they just asked to be rid of — and comes back at the next start, which the error says.
The in-use check and the removal are not one atomic step: something that picks the connection in between gets a connect that fails ("unknown connection"), or a pool the Drop then closes. Either shows as an error there and breaks nothing else.
func (*Editor) Edit ¶
Edit changes a connection added from inside dbc — its name, driver, DSN, TLS or ai_rows — in place. An empty DSN keeps the stored one (FormDSN).
Anything but ai_rows changes what the connection connects to, or what it is called, and whatever is on it holds a session opened under the old name and DSN; inUse (nil: nothing ever is) is asked then, and its error refuses the edit. An ai_rows change alone goes through regardless.
func (*Editor) FileRefusal ¶
FileRefusal is the answer to changing or removing a connection that is not dbc's to change: the config file's, or a built-in demo. verb is what was asked ("change", "remove").
func (*Editor) FormDSN ¶
FormDSN settles f.DSN, whichever way the form gave it: built from fields, or kept from the stored connection from ("" when adding).
func (*Editor) Test ¶
Test opens the form's DSN, pings it within connect_timeout (capped at TestTimeoutCap when that is "no limit") and closes it again: nothing is saved, and no pool is left behind for a connection that may never be added. from is the connection being edited ("" when adding), whose stored DSN an empty one means (see FormDSN). The error is the form's — one that could never work; a connect that fails is a TestResult with ProbeErr set.
func (*Editor) UnsavedWarning ¶
UnsavedWarning is the note for a connection change that no file keeps — there is no home directory to keep one in: it holds only until this program stops. what is the thing that lasts that long.
type Error ¶
Error is a refusal the user can act on. Its message is written for the user, not the log. Any other error from this package is a failure to read or write the saved-connections file (500 in the web).
type Form ¶
type Form struct {
Name string `json:"name"`
Driver string `json:"driver"`
DSN string `json:"dsn"`
AIRows bool `json:"ai_rows"`
// TLS as typed: ${VAR}s unexpanded and a relative path relative, as the
// saved file keeps them (config.SavedConn); TLSFor resolves them.
config.TLSOpts
// Parts, when set, is the DSN as fields; it replaces DSN (see FormDSN).
Parts *db.DSNParts `json:"parts,omitempty"`
// KeepPassword: editing in fields, the password field was left empty
// over a stored password the form was not given — keep that one.
KeepPassword bool `json:"keep_password,omitempty"`
}
Form is the add- or edit-connection form, as either UI collects it.
type Kind ¶
type Kind int
Kind says what sort of refusal an Error is, which each UI maps to its own words — dbc web to an HTTP status.
type TestResult ¶
type TestResult struct {
OK bool
Took time.Duration // open + ping, when it connected
Note string // a qualified yes ("the file does not exist yet …")
ProbeErr error // the driver's refusal; nil when it connected
Warnings []string // unset ${VAR}s and the like
}
TestResult is the answer to "does this connect?". A connect that fails is not an error of the test — the test worked, and this is its answer — so it is ProbeErr here, not Test's error.
func (TestResult) ProbeMsg ¶
func (r TestResult) ProbeMsg() string
ProbeMsg is the words for a failed test's ProbeErr.