Documentation
¶
Overview ¶
Package cliout centralizes the machine-facing stdout protocol shared by the compat CLIs. Every CLI emits a single-line JSON result on success and, on any failure, a single-line typed error JSON on stdout:
{"status":"error","code":"<CODE>","message":"<detalle>"}
An agent can parse stdout line-by-line and branch on the "code" field without scraping free-text stderr. The taxonomy is closed: callers pick the most specific applicable code from the constants below. The public compat/ API is not extended here; errors are classified by phase (which step failed) and, for replication, by errors.As against the existing compat.ConflictError.
Index ¶
- func DecodeFileStrict(path string, v any) error
- func Die(code ErrorCode, err error)
- func DispatchUsageMessage(firstArg string) string
- func EmitError(code ErrorCode, message string) int
- func EmitErrorFrom(code ErrorCode, err error) int
- func EmitJSON(v any) error
- func ExitCode(code ErrorCode) int
- func ParseArgsStrict(knownFlags, args []string, wantN int, ...) (present map[string]bool, positional []string)
- func ResolveSchema(configPath, ref string, inline compat.Schema) (compat.Schema, error)
- func SplitArgs(knownFlags []string, args []string) (present map[string]bool, positional []string, unexpected string, ...)
- type ErrorCode
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeFileStrict ¶
DecodeFileStrict reads path and decodes it into v using a json.Decoder with DisallowUnknownFields, so an unknown key is an explicit error instead of being silently dropped. This closes the silent-degradation gap: a typo'd or unsupported config key is reported (ERR_CONFIG), not ignored, matching the "never silently degrade" principle the project promises.
func Die ¶
Die writes err to stderr, emits the typed error envelope for err on stdout, and exits with the code's canonical process status. It is the non-returning counterpart to EmitErrorFrom and centralizes the "log to stderr, emit envelope to stdout, os.Exit" pattern shared by every compat CLI's error path. A nil err is rendered as the code itself (mirroring EmitErrorFrom); callers always pass a non-nil error in practice.
Die does not return, so any code after a Die call is unreachable to the compiler's flow analysis — there is no need for an explicit return statement following it.
func DispatchUsageMessage ¶
DispatchUsageMessage returns the ERR_USAGE envelope message for the top-level compat dispatch when the leading token is not a known subcommand. A leading token that begins with "-" and is not a help-ish token (--help/-h/-help) means a flag was placed before the subcommand; the message orients the user to put flags after the subcommand (e.g. `compat cutover --dry-run <config>`). Every other unrecognized leading token (an unknown subcommand name, an empty arg, or a help-ish flag) keeps the generic "missing or unknown subcommand" message. The caller wraps this in Die(ErrUsage, errors.New(msg)).
helpIsh is a fixed set here (not a parameter) so the dispatch and this helper agree on exactly which leading tokens are treated as help requests rather than misplaced flags.
func EmitError ¶
EmitError writes a single-line typed error JSON to stdout and returns the exit code the caller should pass to os.Exit. The message is JSON-encoded, so embedded newlines in the underlying error never break the one-line contract.
func EmitErrorFrom ¶
EmitErrorFrom writes the typed error JSON for err using code and returns the exit code. A nil err is rendered as the code itself.
func EmitJSON ¶
EmitJSON marshals v to compact, single-line JSON and writes it to stdout with a trailing newline. It is the success-path counterpart to EmitError.
func ExitCode ¶
ExitCode returns the process exit code for a typed error. Incorrect usage is exit 2; every other failure is exit 1, preserving each CLI's existing codes.
func ParseArgsStrict ¶
func ParseArgsStrict(knownFlags, args []string, wantN int, hint, unexpectedMsg, duplicateMsg, countMsg string) (present map[string]bool, positional []string)
ParseArgsStrict is the shared front-end of every compat CLI: it partitions args into known boolean flags and positional arguments, rejects any unknown leading-dash token as ERR_USAGE (exit 2), rejects a duplicated recognized flag as ERR_USAGE (exit 2), and requires exactly wantN positional arguments. On a violation it prints hint to stderr, emits the ERR_USAGE envelope to stdout, and exits — it never returns on a violation. On success it returns the recognized flags that were seen and the positional tokens in order; the caller owns any flag-specific logic (e.g. --dry-run).
hint is the stderr usage hint printed before the envelope; unexpectedMsg is the envelope message for an unexpected flag (formatted with %q and the offending token); duplicateMsg is the envelope message for a repeated recognized flag (formatted with %q and the offending token); countMsg is the envelope message for a wrong positional count. They are caller-supplied so each CLI keeps its existing, documented usage strings and envelope messages byte-for-byte — this helper only removes the duplicated SplitArgs + fmt.Fprintln + os.Exit plumbing, never the observable text or exit codes.
func ResolveSchema ¶
ResolveSchema picks the canonical schema for a cutover/migration config from exactly one of the inline `schema` field or a `schema_ref` path. inline is the decoded inline schema; ref is the schema_ref value. Exactly one must be populated: inline is "populated" when it declares at least one table, and ref is "populated" when non-empty. Both or neither is an error (ERR_CONFIG). When ref is set, the referenced schema file is loaded relative to configPath. The returned error is meant for ERR_CONFIG.
Treating an empty inline schema (zero tables) as "absent" is what stops a config that omits both from running with no schema at all — the prior silent-degradation bug.
func SplitArgs ¶
func SplitArgs(knownFlags []string, args []string) (present map[string]bool, positional []string, unexpected string, duplicate string, ok bool)
SplitArgs partitions a CLI argument list into recognized boolean flags and positional arguments. Every flag in this CLI surface is value-less (e.g. `--dry-run`), so flags never consume the following token.
A bare "--" token is the standard end-of-flags separator: once seen, every later token is positional even if it begins with "-" (so `compat audit -- --raro.json` treats `--raro.json` as the config path), and "--" itself is discarded. This lets a user pass a config path that starts with "-".
A token that begins with "-" (and is not "--") and is not one of knownFlags is an unexpected flag: ok is false and unexpected is the offending token, so the caller emits ErrUsage (exit 2). This makes ERR_USAGE actually cover "unexpected flag" as the docs claim, instead of letting an unknown flag fall through to the positional config path. A recognized flag seen more than once (e.g. `--dry-run --dry-run`) is a duplicate: ok is false and duplicate is the offending token, so the caller emits ErrUsage (exit 2) with a "duplicate flag" message — a repeated boolean flag usually signals a user mistake that should not pass unnoticed.
present holds the recognized flags that were seen (empty when knownFlags is empty, e.g. for the audit/copy subcommands). positional holds the non-flag tokens in order; the caller still validates the expected count.
Types ¶
type ErrorCode ¶
type ErrorCode string
ErrorCode is one value from the closed CLI error taxonomy.
const ( // ErrUsage is incorrect CLI invocation (wrong argument count); exit 2. ErrUsage ErrorCode = "ERR_USAGE" // ErrConfig is an unreadable or invalid JSON config (file read, unmarshal, // or contract validation). Exit 1. ErrConfig ErrorCode = "ERR_CONFIG" // ErrAuditNotExact is a required feature whose audit status is not exact. // The feature findings are still emitted before this error line. Exit 1. ErrAuditNotExact ErrorCode = "ERR_AUDIT_NOT_EXACT" // ErrConnectSource is a failure to reach or open the source store. Exit 1. ErrConnectSource ErrorCode = "ERR_CONNECT_SOURCE" // ErrConnectDestination is a failure to reach or open the destination store. Exit 1. ErrConnectDestination ErrorCode = "ERR_CONNECT_DESTINATION" // ErrSchema is a schema validation or ApplySchema failure. Exit 1. ErrSchema ErrorCode = "ERR_SCHEMA" // ErrSnapshot is an export/import snapshot failure. Exit 1. ErrSnapshot ErrorCode = "ERR_SNAPSHOT" // ErrReplicationConflict is a compat.ConflictError raised while replaying // the change journal during catch-up. Exit 1. ErrReplicationConflict ErrorCode = "ERR_REPLICATION_CONFLICT" // ErrCapture is a change-capture install or read failure. Exit 1. ErrCapture ErrorCode = "ERR_CAPTURE" // ErrVerifyDiverged is a digest mismatch at verification. The CLI still // emits its diverged result JSON with this code. Exit 1. ErrVerifyDiverged ErrorCode = "ERR_VERIFY_DIVERGED" // ErrInternal is any failure not covered by a more specific code. Exit 1. ErrInternal ErrorCode = "ERR_INTERNAL" )
func ReplicationCode ¶
ReplicationCode classifies an error from the catch-up drain loop. A true ConflictError raised while applying the journal is ErrReplicationConflict; anything else from the drain is ErrInternal. Capture-install and capture-read failures are classified by the caller as ErrCapture before they reach this helper.