4bdfce6250
Per-form hints for the 14 app-lifecycle commands (quit/help/hint/ rebuild/save/new/load/export/import/mode/messages/undo/redo/copy), reference-leaning what/example with concept where it teaches (rebuild, mode, messages, undo, export, help). hint_ids wired, catalogue + keys.rs registered. +1 spot test; 2489 pass / 1 ignored, clippy clean.
1183 lines
64 KiB
YAML
1183 lines
64 KiB
YAML
# en-US catalog (ADR-0019).
|
||
#
|
||
# Hierarchical groups flatten to dot-paths internally:
|
||
# error.unique.insert.headline
|
||
# error.unique.insert.hint
|
||
# help.cli_banner
|
||
# replay.completed
|
||
# … etc.
|
||
#
|
||
# Each error entry has a `headline` (one line, used in both
|
||
# short and verbose modes) and may have a `hint` (one or more
|
||
# lines, surfaced only in verbose mode). Short mode = headline
|
||
# only; verbose mode = headline + hint + (if present) the
|
||
# diagnostic table the translator built (ADR-0019 §7).
|
||
#
|
||
# Anchor phrases per ADR-0019 §10 are kept stable across
|
||
# wording changes:
|
||
# "no such table"
|
||
# "no such column"
|
||
# "no such relationship"
|
||
# "already exists"
|
||
# "already has the value"
|
||
# "cannot be converted"
|
||
# "discard information"
|
||
# "referenced by"
|
||
# "[client-side]"
|
||
#
|
||
# Placeholders use `{name}` substitution; format specifiers
|
||
# (`{name:08.2}`, `{name:>10}`, …) are explicitly rejected by
|
||
# the substitution helper (ADR-0019 §8.4).
|
||
|
||
# Sanity entry exercised by the loader's unit tests; not
|
||
# user-facing.
|
||
_test:
|
||
hello: "Hello, {name}!"
|
||
|
||
# ---- Error category --------------------------------------------------
|
||
error:
|
||
|
||
# UNIQUE constraint violations. Anchor: "already has the value".
|
||
unique:
|
||
insert:
|
||
headline: "`{table}.{column}` already has the value `{value}`."
|
||
hint: "The `{column}` column on `{table}` is unique — pick a different value, or update the existing row instead."
|
||
update:
|
||
headline: "`{table}.{column}` already has the value `{value}`."
|
||
hint: "The `{column}` column on `{table}` is unique — your update would create a duplicate."
|
||
# Primary-key collisions get distinct wording — the user
|
||
# learns that PK is the canonical unique constraint.
|
||
pk:
|
||
insert:
|
||
headline: "`{table}` already has a row with primary key `{value}`."
|
||
hint: "Primary keys must be unique — pick a different value or update the existing row."
|
||
update:
|
||
headline: "`{table}` already has a row with primary key `{value}`."
|
||
hint: "Primary keys must be unique — your update would create a duplicate."
|
||
|
||
# FOREIGN KEY violations. Anchor: "referenced by".
|
||
foreign_key:
|
||
# Child-side: insert/update sets a value that has no parent.
|
||
child_side:
|
||
insert:
|
||
headline: "no parent row in `{parent_table}` has `{parent_column}` = `{value}`."
|
||
hint: "Foreign keys must point at an existing parent row. Insert a matching parent first, or pick a value that already exists in `{parent_table}.{parent_column}`."
|
||
update:
|
||
headline: "no parent row in `{parent_table}` has `{parent_column}` = `{value}`."
|
||
hint: "Foreign keys must point at an existing parent row. Pick a value that already exists in `{parent_table}.{parent_column}`."
|
||
# Parent-side: delete/update on a row referenced by children.
|
||
# The engine refuses unless the relationship's `on delete /
|
||
# on update` says cascade or set null.
|
||
parent_side:
|
||
delete:
|
||
headline: "`{table}` rows are referenced by `{child_table}`."
|
||
hint: "Deleting these rows would orphan the children. Delete the children first, or change the relationship's `on delete` action to `cascade` or `set null`."
|
||
update:
|
||
headline: "`{table}` rows are referenced by `{child_table}`."
|
||
hint: "Updating the referenced column would orphan the children. Update the children first, or change the relationship's `on update` action to `cascade`."
|
||
|
||
# NOT NULL constraint violations.
|
||
not_null:
|
||
insert:
|
||
headline: "`{table}.{column}` cannot be null."
|
||
hint: "The `{column}` column is required — provide a value for it in the row you are inserting."
|
||
update:
|
||
headline: "`{table}.{column}` cannot be null."
|
||
hint: "The `{column}` column is required — pick a non-null value, or do not include `{column}` in your `set` list."
|
||
|
||
# CHECK constraint violations (ADR-0029 §10). When the
|
||
# runtime resolves the column's compiled CHECK expression,
|
||
# `hint_with_rule` names both the offending value and the
|
||
# rule; the plain `hint` is the fallback when enrichment
|
||
# could not resolve the rule.
|
||
check:
|
||
insert:
|
||
headline: "check constraint refused `{table}.{column}`."
|
||
hint: "A check constraint requires `{column}` to satisfy a rule the inserted value did not."
|
||
hint_with_rule: "The value {value} breaks the rule `{rule}` — `{column}` must satisfy it."
|
||
update:
|
||
headline: "check constraint refused `{table}.{column}`."
|
||
hint: "A check constraint requires `{column}` to satisfy a rule the new value did not."
|
||
hint_with_rule: "The new value {value} breaks the rule `{rule}` — `{column}` must satisfy it."
|
||
|
||
# Type mismatch — engine-side STRICT refusal of a wrong-shape
|
||
# value. Mostly the `change column ... --dont-convert` path
|
||
# today (subsumes `friendly_change_column_engine_error`).
|
||
type_mismatch:
|
||
change_column:
|
||
headline: "cannot change `{table}.{column}` from `{src_type}` to `{target_type}` with `--dont-convert`."
|
||
hint: "The database refused at least one cell as the wrong shape for `{target_type}`. Re-run without `--dont-convert` to see which rows."
|
||
insert:
|
||
headline: "value `{value}` is not a `{expected_type}`."
|
||
hint: "The `{column}` column on `{table}` is `{expected_type}` — provide a `{expected_type}` value, or change the column's type with `change column`."
|
||
update:
|
||
headline: "value `{value}` is not a `{expected_type}`."
|
||
hint: "The `{column}` column on `{table}` is `{expected_type}` — provide a `{expected_type}` value, or change the column's type with `change column`."
|
||
|
||
# Object-not-found errors. Anchor: "no such ...". These are
|
||
# genuinely single-line errors — no hint adds value.
|
||
not_found:
|
||
table:
|
||
headline: "no such table: `{name}`"
|
||
column:
|
||
headline: "no such column: `{table}.{column}`"
|
||
column_unqualified:
|
||
headline: "no such column: `{column}`"
|
||
relationship:
|
||
headline: "no such relationship: `{name}`"
|
||
|
||
# Name-collision errors. Anchor: "already exists".
|
||
already_exists:
|
||
table:
|
||
headline: "table `{name}` already exists"
|
||
column:
|
||
headline: "column `{table}.{column}` already exists"
|
||
relationship:
|
||
headline: "relationship `{name}` already exists"
|
||
|
||
# Generic catch-all when the translator can't classify the
|
||
# engine error into a known category. The wording stays
|
||
# engine-neutral; the message text from the engine is NOT
|
||
# surfaced (ADR-0002 user-facing posture).
|
||
generic:
|
||
headline: "the database refused this `{operation}`."
|
||
hint: "The operation could not be completed against the current state of `{table}`."
|
||
# Used when no table is in context (e.g. contextless `friendly_message()`
|
||
# callsites: replay, undo, rebuild, export) so the hint never leaks a
|
||
# literal `{table}` placeholder.
|
||
hint_no_table: "The operation could not be completed against the current database state."
|
||
|
||
# Errors that are specifically about value validation
|
||
# (DbError::InvalidValue) — wrong arity, wrong literal
|
||
# form, etc. Pre-engine; the catalog covers what the
|
||
# parser / validator already decided was wrong.
|
||
invalid_value:
|
||
arity:
|
||
headline: "expected {expected} value(s), got {actual}."
|
||
empty_insert:
|
||
headline: "INSERT requires at least one column value."
|
||
empty_update:
|
||
headline: "UPDATE requires at least one assignment."
|
||
|
||
# ---- Help text (CLI banner + in-app `help` command) ------------------
|
||
# ---- CLI argument-parsing errors (stderr before TUI starts) ---------
|
||
cli:
|
||
missing_value: "missing value for --{flag}"
|
||
invalid_value: "invalid value for --{flag}: {value} (expected one of: {expected})"
|
||
unknown_argument: "unknown argument: {arg}"
|
||
multiple_paths: "only one project path may be supplied; got both `{first}` and `{second}`"
|
||
resume_with_path: |-
|
||
--resume and a positional <project-path> are mutually exclusive; pass one or the other
|
||
|
||
help:
|
||
# CLI usage banner. Printed by `--help` / `-h` and on
|
||
# argument-parse failure. Multi-line block; consumers
|
||
# iterate lines or print as-is.
|
||
cli_banner: |
|
||
rdbms-playground — a TUI playground for relational database concepts
|
||
|
||
Usage:
|
||
rdbms-playground [options] [<project-path>]
|
||
|
||
Arguments:
|
||
<project-path> Path to an existing project directory.
|
||
Without this, a fresh auto-named temp
|
||
project is created in the data dir.
|
||
|
||
Options:
|
||
-h, --help Print this help and exit.
|
||
--theme <light|dark> Override theme (default: auto-detect).
|
||
--data-dir <PATH> Use PATH as the data root instead of
|
||
the OS-standard location for this run.
|
||
--log-file <PATH> Write tracing output to PATH.
|
||
--resume Open the most-recently-used project
|
||
(path tracked under <data-root>/last_project).
|
||
Errors out if no previous project is
|
||
recorded. Mutually exclusive with
|
||
<project-path>.
|
||
--no-undo Disable the undo machinery for this run:
|
||
no snapshot is taken before each change
|
||
(no per-command overhead), and undo/redo
|
||
report that undo is turned off.
|
||
--mode <simple|advanced>
|
||
Start in this input mode, overriding the
|
||
project's stored mode. Without it, the
|
||
project's last-used mode is restored
|
||
(default: simple).
|
||
--demo Demonstration mode: show on-screen badges
|
||
for otherwise-invisible keys (Tab, Enter,
|
||
...) — for screencasts and live teaching.
|
||
|
||
App-level commands (typed inside the app, available in both modes):
|
||
quit Exit cleanly.
|
||
mode simple|advanced Switch input mode.
|
||
help Show this list of commands in-app.
|
||
save Save the current temp project under a
|
||
chosen name (or `save as` to copy a
|
||
named project to a new location).
|
||
save as Always prompt for a target name/path.
|
||
new Close current, create a fresh temp.
|
||
load Open the project picker.
|
||
rebuild Rebuild playground.db from project.yaml
|
||
+ data/, with confirmation.
|
||
export [<path>] Write a zip of project.yaml + data/ to
|
||
<path> (relative paths under the data
|
||
root). Excludes playground.db and
|
||
history.log.
|
||
import <zip> [as <t>] Unpack <zip> into a new project and
|
||
switch to it. <t> overrides the target
|
||
name (else taken from the zip).
|
||
copy [all|last] Copy the output panel to the system
|
||
clipboard (`copy last` copies just the
|
||
most recent command's output).
|
||
# In-app `help` command output (ADR-0024 §help_id). The
|
||
# renderer iterates the command REGISTRY and translates each
|
||
# CommandNode's `help_id` — so a newly-registered command
|
||
# appears in `help` automatically, with no edit here. `intro`
|
||
# and `dsl_section` frame the two command groups; each
|
||
# `app.*` / `ddl.*` / `data.*` entry is keyed by a command's
|
||
# `help_id`; `types_reference` closes the block. All entries
|
||
# are multi-line-capable — the renderer emits one output row
|
||
# per line so scroll math stays accurate.
|
||
intro: "Supported commands:"
|
||
dsl_section: "DSL data commands (in simple mode):"
|
||
# H3: footer on the full `help` list, and the not-found note
|
||
# for `help <topic>`. `{topic}` is the word the user typed.
|
||
detail_hint: "Type `help <command>` for detail on one command (e.g. `help insert`), or `help types` for the type reference."
|
||
unknown_topic: "No help for `{topic}`. Type `help` for the full command list, or `help types` for the type reference."
|
||
# Per-command help, keyed by `CommandNode.help_id`. Block
|
||
# scalars (`|-`) so the column alignment survives — the
|
||
# double-quoted form trips a libyml scanner bug on long
|
||
# internal space runs. The renderer emits one output row per
|
||
# line, so multi-form commands list each form on its own line.
|
||
app:
|
||
quit: |-
|
||
quit — exit the app
|
||
help: |-
|
||
help — show this command list
|
||
help <command> — detailed help for one command (e.g. `help insert`)
|
||
hint: |-
|
||
hint — explain the most recent error (press F1 for a hint on what you're typing)
|
||
rebuild: |-
|
||
rebuild — rebuild the project database from project.yaml + data/ (with confirmation)
|
||
save: |-
|
||
save — save the current temp project under a name
|
||
save as — copy the current project to a new name/path
|
||
new: |-
|
||
new — close the current project, start a fresh temp project
|
||
load: |-
|
||
load — open the project picker
|
||
export: |-
|
||
export [<path>] — write a zip of project.yaml + data/ (excludes the database file and history.log)
|
||
import: |-
|
||
import <zip> [as <target>] — unpack a zip into a new project and switch to it
|
||
mode: |-
|
||
mode simple|advanced — switch input mode
|
||
messages: |-
|
||
messages [short|verbose] — show or switch error-message verbosity (verbose is the default)
|
||
undo: |-
|
||
undo — undo the last change (with confirmation)
|
||
redo: |-
|
||
redo — redo the last undone change (with confirmation)
|
||
copy: |-
|
||
copy [all|last] — copy the output panel to the clipboard (`copy last` = the most recent command)
|
||
ddl:
|
||
create: |-
|
||
create table <T> with pk [<col>(<type>), ...] — create a table
|
||
create_m2n: |-
|
||
create m:n relationship from <T1> to <T2> [as <name>]
|
||
— build a junction table linking two tables
|
||
sql_create_table: |-
|
||
create table [if not exists] <T> (
|
||
<col> <type> [not null] [unique] [primary key] [default <expr>] [check (<expr>)] [references <P>[(<col>)]], ...
|
||
[, primary key (<col>, ...)] [, unique (<col>, ...)] [, check (<expr>)]
|
||
[, [constraint <name>] foreign key (<col>) references <P>[(<col>)]])
|
||
— create a table (advanced SQL)
|
||
sql_drop_table: |-
|
||
drop table [if exists] <T> — remove a table (advanced SQL)
|
||
sql_create_index: |-
|
||
create [unique] index [if not exists] [<name>] on <T> (<col>, ...)
|
||
— create an index (advanced SQL)
|
||
sql_drop_index: |-
|
||
drop index [if exists] <name> — remove an index (advanced SQL)
|
||
sql_alter_table: |-
|
||
alter table <T> add column <col> <type> [not null] [unique] [default …] [check …]
|
||
alter table <T> drop column <col>
|
||
alter table <T> rename column <old> to <new>
|
||
alter table <T> rename to <new>
|
||
alter table <T> alter column <col> type <type>
|
||
alter table <T> add [constraint <name>] check (<expr>) | unique (<col>, …) | foreign key (<col>) references <P>[(<col>)]
|
||
alter table <T> drop constraint <name> — evolve a table's columns and constraints (advanced SQL)
|
||
drop: |-
|
||
drop table <T> — remove a table
|
||
drop column [from] [table] <T>: <col> [--cascade] — remove a column
|
||
(--cascade also drops any index that covers the column)
|
||
drop relationship <name> — remove a relationship
|
||
drop index <name> — remove an index
|
||
drop index on <T> (<col>, ...) — remove an index by its columns
|
||
add: |-
|
||
add column [to] [table] <T>: <col> (<type>) — add a column
|
||
(for serial/shortid on a non-empty table: existing rows auto-filled)
|
||
add 1:n relationship [as <name>] from <P>.<col> to <C>.<col>
|
||
[on delete <action>] [on update <action>] [--create-fk] — declare a relationship
|
||
add index [as <name>] on <T> (<col>, ...) — create an index
|
||
rename: |-
|
||
rename column [in] [table] <T>: <old> to <new> — rename a column
|
||
change: |-
|
||
change column [in] [table] <T>: <col> (<newtype>) [--force-conversion | --dont-convert]
|
||
— change a column's type (to serial/shortid: null cells auto-filled with generated values)
|
||
data:
|
||
show: |-
|
||
show table <T> — show a table's structure
|
||
show data <T> — show a table's rows
|
||
show tables — list all tables
|
||
show relationships — list all relationships
|
||
show indexes — list all indexes
|
||
show relationship <name> — show one relationship's detail
|
||
show index <name> — show one index's detail
|
||
seed: |-
|
||
seed <T> [<count>] — fill a table with generated sample rows
|
||
(default 20). Existing rows are kept;
|
||
foreign keys draw from existing parent rows.
|
||
seed <T> ... set <c> = 'v' | in ('a','b') | as <gen> | between x and y
|
||
— pin how a column is generated: a fixed
|
||
value, a pick-list, a named generator
|
||
(email, name, product, ...), or a range.
|
||
seed <T>.<col> [set ...] — fill one column across the EXISTING rows
|
||
(the follow-up to `add column`).
|
||
seed <T> ... --seed <n> — reproducible: same data for the same n.
|
||
insert: |-
|
||
insert into <T> [(cols)] [values] (vals) — add a row
|
||
update: |-
|
||
update <T> set <c>=<v>, ... where <c>=<v> | --all-rows — change matching rows
|
||
delete: |-
|
||
delete from <T> where <c>=<v> | --all-rows — remove matching rows
|
||
replay: |-
|
||
replay <path> — run each non-blank, non-`#`-comment line of <path>
|
||
as a command. Stops at the first error (no rollback);
|
||
relative paths resolve under the project directory.
|
||
explain: |-
|
||
explain show data <T> | explain update <T> ... | explain delete from <T> ...
|
||
— show how the database would run a query, without
|
||
running it (safe even for update / delete)
|
||
explain <select|with|insert|update|delete …> (advanced mode)
|
||
— the same plan for the SQL you wrote
|
||
# Type reference, appended after the command list.
|
||
types_reference: |
|
||
Types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid
|
||
Auto-generated types (serial, shortid):
|
||
serial — integer that auto-fills with the next sequence value
|
||
(MAX(col)+1) on insert. Outside a primary key it carries
|
||
a UNIQUE contract.
|
||
shortid — short base58 identifier auto-filled at insert time. Always
|
||
carries a UNIQUE contract.
|
||
Adding or changing-to either type on a non-empty table auto-fills
|
||
existing/null cells in the same operation.
|
||
|
||
# ---- DSL parse error rendering --------------------------------------
|
||
# ---- Hint panel ambient typing assistance (ADR-0022 §6) -------------
|
||
hint:
|
||
# The hint panel goes ambient as soon as the user types
|
||
# anything — empty input keeps the existing
|
||
# `panel.hint_empty` content.
|
||
# Hint sentences are full standalone phrases, capitalised
|
||
# at the start. Inline `{message}` substitutions inherit
|
||
# whatever case the source produced (parser errors,
|
||
# engine messages) — they're embedded mid-sentence so they
|
||
# stay lowercase by convention.
|
||
ambient_complete: "Submit with Enter"
|
||
ambient_expected: "Next: {expected}"
|
||
ambient_error_with_usage: "{message} — usage: {usage}"
|
||
# H2 / ADR-0053: shown by `hint` / F1 when there is nothing specific
|
||
# to expand on (no recent error, empty input).
|
||
getting_started: "Start typing a command and press F1 for a hint, or type `help` for the full command list."
|
||
# ── Tier-3 teaching blocks (ADR-0053 D3) ──────────────────────────
|
||
# Per-form command hints (`hint.cmd.<form>`) and per-class error
|
||
# hints (`hint.err.<class>`), each a `what` (1–2 sentences) / `example`
|
||
# (one runnable, mode-correct line) / `concept` (the relational idea —
|
||
# the teaching part). Phase B seeds the three approved exemplars; the
|
||
# rest are authored in Phase C.
|
||
cmd:
|
||
insert:
|
||
what: "Add one or more rows to a table."
|
||
example: "insert into Customers values ('Ann', 'ann@example.io')"
|
||
concept: "A row is one record; each value lines up with a column, in order. Columns typed `serial`/`shortid` fill themselves — leave them out."
|
||
add_relationship:
|
||
what: "Link two tables so a parent row can own many child rows."
|
||
example: "add 1:n relationship from Customers.id to Orders.customer_id"
|
||
concept: "The \"1:n\" means one parent, many children. The child column holds the foreign key; add `--create-fk` to create that column if it doesn't exist yet."
|
||
# App-lifecycle commands (Phase C batch 1). Reference-leaning, so
|
||
# `concept` appears only where there's a real idea to teach.
|
||
quit:
|
||
what: "Leave the playground. Your project is already saved to disk."
|
||
example: "quit"
|
||
help:
|
||
what: "List every command, or show the detail for one."
|
||
example: "help insert"
|
||
concept: "`help` is the reference; press F1 while typing for a hint about the command you're building right now."
|
||
hint:
|
||
what: "Explain the most recent error — or, pressing F1 while typing, the command you're building."
|
||
example: "hint"
|
||
rebuild:
|
||
what: "Rebuild the project database from its saved text files."
|
||
example: "rebuild"
|
||
concept: "The text files (project.yaml + the data folder) are the source of truth; the database is derived and can always be rebuilt from them."
|
||
save:
|
||
what: "Save the current project under a name; `save as` copies it to a new one."
|
||
example: "save as my-shop"
|
||
new:
|
||
what: "Close the current project and start a fresh temporary one."
|
||
example: "new"
|
||
load:
|
||
what: "Open the project picker to switch to a saved project."
|
||
example: "load"
|
||
export:
|
||
what: "Write a shareable zip of the project — its text files only, never the database."
|
||
example: "export my-shop.zip"
|
||
concept: "The zip carries the schema and data as text, so anyone can rebuild the very same database from it."
|
||
import:
|
||
what: "Unpack a project zip into a new project and switch to it."
|
||
example: "import my-shop.zip as shop-copy"
|
||
mode:
|
||
what: "Switch between simple mode (the guided teaching commands) and advanced mode (raw SQL)."
|
||
example: "mode advanced"
|
||
concept: "Simple mode uses keyword commands; advanced mode lets you write SQL directly. A leading `:` runs a single advanced command without switching modes."
|
||
messages:
|
||
what: "Show or set how much detail error messages give."
|
||
example: "messages short"
|
||
concept: "Verbose (the default) adds a fix-it hint under each error headline; short shows just the headline."
|
||
undo:
|
||
what: "Undo the most recent change, after a confirmation."
|
||
example: "undo"
|
||
concept: "Every data or schema change is snapshotted first, so you can step back; `redo` re-applies what you undid."
|
||
redo:
|
||
what: "Re-apply the most recently undone change."
|
||
example: "redo"
|
||
copy:
|
||
what: "Copy the output panel to the clipboard — all of it, or just the last command's output."
|
||
example: "copy last"
|
||
err:
|
||
foreign_key:
|
||
child_side:
|
||
what: "The value you gave for the child column doesn't match any parent row, so the foreign key has nothing to point at."
|
||
example: "First insert the parent (insert into Customers …), then the child that references it."
|
||
concept: "A foreign key is a promise that every child points at a real parent, so the parent must exist first. To allow orphans on delete instead, set the relationship's `on delete` to `set null` or `cascade`."
|
||
# Invalid identifier in a schema slot (ADR-0022 stage 8e
|
||
# + the user's #5). Voice mirrors ADR-0019's "no such
|
||
# {kind}" wording for consistency with engine errors.
|
||
ambient_invalid_ident: "No such {kind}: `{found}`"
|
||
# User-invented-name slot (NewName per IdentSlot). The
|
||
# probe-derived `{next}` is what comes after the name —
|
||
# e.g. `(` after a new column name. Empty/unknown `next`
|
||
# falls through to `ambient_typing_name` instead.
|
||
ambient_typing_name: "Type a name"
|
||
ambient_typing_name_then: "Type a name, then {next}"
|
||
# Issue #4 — advanced-mode CREATE TABLE element slot. Surfaced
|
||
# at `create table T (` so the column-name role is visible
|
||
# alongside the table-level constraint keywords.
|
||
create_table_element: "Type a column name, or a table-level constraint: `primary`, `unique`, `check`, `constraint`, `foreign`"
|
||
# Issue #26: the `seed <table> ▮` position. The optional row count is
|
||
# a bare number with no Tab candidate, so it (and the `.column`
|
||
# column-fill form) would be invisible next to the `set` / `--seed`
|
||
# chips. Names every option so the most common next move (a count) is
|
||
# discoverable.
|
||
seed_count: "Optionally a row count, e.g. `50` (default 20); `.column` to fill one column on existing rows; `set` to pin a column; `--seed` to fix the RNG"
|
||
# Value-literal slot — `insert ... values (`, `update ... set
|
||
# col=`, `where col=`. Replaces the misleading "null true
|
||
# false" keyword candidate list with format guidance for all
|
||
# accepted literal forms. Used when the walker can't resolve a
|
||
# column type (schemaless parse, missing table, unknown column).
|
||
value_literal_slot: "Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')"
|
||
# Per-column-type value-slot hints (ADR-0024 §Phase D §typed-value-slots).
|
||
# Fired when the walker resolved the column's user-facing type
|
||
# at the current value slot; narrows the prose to the relevant
|
||
# literal forms for that type. Falls back to
|
||
# `value_literal_slot` when the type can't be resolved.
|
||
value_slot_int: "Type an integer (e.g. 42, -7) or null"
|
||
value_slot_real: "Type a number (e.g. 3.14, -0.5) or null"
|
||
value_slot_decimal: "Type a number (e.g. 19.95, -2.50) or null"
|
||
value_slot_bool: "Type true, false, or null"
|
||
value_slot_text: "Type a quoted string (e.g. 'Alice') or null"
|
||
value_slot_date: "Type a quoted date as 'YYYY-MM-DD' or null"
|
||
value_slot_datetime: "Type a quoted datetime as 'YYYY-MM-DD HH:MM:SS' or null"
|
||
value_slot_blob: "Type a quoted blob literal or null"
|
||
# Serial / shortid in `values (…)` form: the user must enter
|
||
# something at this position (no "skip column" syntax). `null`
|
||
# is the auto-fill path (ADR-0018: serial / shortid columns
|
||
# auto-fill null cells on insert), so the prose leads with
|
||
# null and offers an explicit value as the alternative.
|
||
value_slot_serial: "Type null to auto-generate, or an explicit integer"
|
||
value_slot_shortid: "Type null to auto-generate, or a quoted shortid"
|
||
# Wrapper that prefixes a per-column-type slot hint with the
|
||
# actual column name so the user sees "for `Email`: Type a
|
||
# quoted string …" instead of the generic type prose.
|
||
value_slot_for_column: "for `{column}`: {detail}"
|
||
# Pedagogical note appended at the first value slot of a
|
||
# Form B `insert into T values (…)` when T has auto-generated
|
||
# columns the value list skips — points the user at the
|
||
# explicit-column form so the skipped column is discoverable.
|
||
value_slot_autogen_skipped: "({columns} auto-generated — skipped here; list columns explicitly, e.g. `insert into T (...) values (...)`, to set it.)"
|
||
|
||
# Advanced-mode SQL DDL execution notes (ADR-0035).
|
||
ddl:
|
||
# `create table if not exists <T>` where the table is already
|
||
# present: a no-op that succeeds with this note instead of an
|
||
# "already exists" error.
|
||
create_skipped_exists: "table '{name}' already exists — skipped (no changes made)"
|
||
# `drop table if exists <T>` where the table is absent: a no-op that
|
||
# succeeds with this note instead of a "doesn't exist" error.
|
||
drop_skipped_absent: "table '{name}' doesn't exist — skipped (no changes made)"
|
||
# `create [unique] index if not exists <name> …` where the index name
|
||
# already exists: a no-op that succeeds with this note (ADR-0035 §4d).
|
||
create_index_skipped_exists: "index '{name}' already exists — skipped (no changes made)"
|
||
# `drop index if exists <name>` where the index is absent: a no-op that
|
||
# succeeds with this note instead of a "doesn't exist" error.
|
||
drop_index_skipped_absent: "index '{name}' doesn't exist — skipped (no changes made)"
|
||
|
||
parse:
|
||
# Wrapper around chumsky's structural error message. The
|
||
# caret pointer (visualising the failure column) is printed
|
||
# on its own preceding line via `parse.caret`.
|
||
error: "parse error: {detail}"
|
||
# Wrapper used by `ParseError::Display` (so any to_string()
|
||
# call on a parse error renders consistently with the in-app
|
||
# error rendering).
|
||
error_wrapper: "could not parse command: {detail}"
|
||
# Custom (try_map / source-slice) error messages raised by
|
||
# the DSL parser. These were hand-written strings in
|
||
# `src/dsl/parser.rs` until the catalog migration brought
|
||
# them under the same roof as the rest of the user-facing
|
||
# vocabulary. Wording is unchanged from the inline source
|
||
# form so existing anchor-phrase tests still match.
|
||
custom:
|
||
replay_path_expected: "expected a path after `replay`"
|
||
create_table_needs_pk: |-
|
||
tables need at least one column. Add `with pk` for a default `id INTEGER PRIMARY KEY`, or `with pk <name>(<type>)` to choose. Use a comma-separated list for compound primary keys.
|
||
# ADR-0026 §1: the recursion-depth guard on the
|
||
# WHERE-expression grammar. Input nested past the cap
|
||
# (`((((…))))`) stops here with a friendly error instead
|
||
# of overflowing the parser stack.
|
||
expression_too_deep: "expression nested too deeply"
|
||
on_action_specified_twice: "`on {target}` specified twice"
|
||
change_column_flags_exclusive: "`--force-conversion` and `--dont-convert` are mutually exclusive — pick one."
|
||
# ADR-0035 §4g: adding a primary key to an existing table is not
|
||
# supported — every table is created with its primary key.
|
||
alter_add_primary_key: "a table's primary key is fixed at creation — `alter table … add primary key` is not supported."
|
||
# ADR-0035 §4g: composite UNIQUE constraints are unnamed in this
|
||
# tool, so a `constraint <name>` prefix on UNIQUE has nowhere to go.
|
||
alter_named_unique: "a UNIQUE constraint cannot be named — use `alter table <T> add unique (<col>, …)` without `constraint <name>`."
|
||
unknown_type: "unknown type '{found}' (expected one of: {expected})"
|
||
unknown_action: "unknown referential action '{found}' (expected one of: {expected})"
|
||
# Phase D typed-value-slot mismatch (ADR-0024 §Phase D):
|
||
# surfaced when a column's value slot rejects the literal
|
||
# the user typed (e.g. `3.14` at an `int` column). `{found}`
|
||
# is the literal text; `{expected}` names the required
|
||
# shape (`integer`, `number`, …).
|
||
bind_type_mismatch: "value '{found}' is not a valid {expected}"
|
||
# `insert into T (col)` with no `values (...)` afterwards
|
||
# — the shared `(…)` opener matched as a Form-A column
|
||
# list but the user hasn't typed the value clause yet.
|
||
# Surfaced as a parse-time error so the input renderer
|
||
# classifies the input as mid-typing rather than
|
||
# dispatching a logically-empty Form C insert.
|
||
insert_form_a_missing_values: "`insert into ...({columns})` looks like Form A — add `values (...)` to supply the matching values."
|
||
# ADR-0029 §9: a primary-key column is already NOT NULL,
|
||
# and a single-column primary key is already UNIQUE —
|
||
# declaring either explicitly is redundant.
|
||
constraint_redundant_on_pk: "`{column}` is a primary-key column, so it is already {constraint} — drop the redundant constraint."
|
||
# Caret pointer showing where in the input the parser
|
||
# failed. `{padding}` is the leading whitespace; the
|
||
# template appends `^` so the rendered line places the
|
||
# marker under the offending character.
|
||
caret: "{padding}^"
|
||
# Default for the `ParseError::Empty` variant — surfaces as
|
||
# `{detail}` inside the wrapper.
|
||
empty: "empty input"
|
||
# No-prefix fallback (ADR-0021 §5): when the parse fails
|
||
# before any keyword is consumed, the renderer lists every
|
||
# command-entry keyword instead of attempting a per-command
|
||
# usage block. `{commands}` is an oxford-joined list of
|
||
# command-keyword renderings (each from
|
||
# `parse.token.keyword.*`).
|
||
available_commands: "available commands: {commands}"
|
||
# ADR-0042 G2: collapse the SELECT projection-start expression
|
||
# first-set (14 expression-starters plus the `distinct`/`all`
|
||
# quantifiers) into one learner-sized gloss in the error
|
||
# message. The detector keys on `distinct` AND `all` being
|
||
# jointly expectable, which only happens at a projection start —
|
||
# so the raw set is replaced *in the error line only*;
|
||
# completion/hints still expand the full first-set.
|
||
expect:
|
||
select_projection: "a projection: `*`, a column, or an expression"
|
||
# ADR-0042 §3: a CROSS JOIN pairs every row and takes no ON
|
||
# clause. The grammar rejects a following `on`; this message
|
||
# (rendered in place of the generic structural error when the
|
||
# most recent join is a CROSS join and the failing token is `on`)
|
||
# teaches the distinction instead of just "expected end of input".
|
||
cross_join_no_on: "a CROSS JOIN has no ON clause — it pairs every row; for a join condition use `JOIN … ON`, or filter with `WHERE`"
|
||
# Per-command usage templates (ADR-0021 §1). Rendered under a
|
||
# "usage:" prefix when a parse fails after consuming a
|
||
# known command-entry keyword. The bracket convention `[...]`
|
||
# marks optional parts; angle-bracket `<...>` marks
|
||
# placeholders. ADR-0009's surface conventions apply.
|
||
usage:
|
||
create_table: "create table <Name> with pk [<col>(<type>)[, ...]]"
|
||
create_m2n: "create m:n relationship from <Table1> to <Table2> [as <Name>]"
|
||
# Terse one-line synopsis (issue #12): the full grammar — every
|
||
# column- and table-level constraint — lives in `help.ddl.sql_create_table`.
|
||
sql_create_table: "create table [if not exists] <Name> (<col> <type> [constraints], ...)"
|
||
sql_drop_table: "drop table [if exists] <Name>"
|
||
sql_create_index: "create [unique] index [if not exists] [<Name>] on <Table> (<col>[, ...])"
|
||
sql_drop_index: "drop index [if exists] <Name>"
|
||
sql_alter_table: |-
|
||
alter table <Table> add column <Name> <Type> [not null] [unique] [default <expr>] [check (<expr>)]
|
||
alter table <Table> drop column <Name>
|
||
alter table <Table> rename column <Old> to <New>
|
||
alter table <Table> rename to <NewName>
|
||
alter table <Table> alter column <Name> type <Type>
|
||
alter table <Table> add [constraint <Name>] check (<expr>) | unique (<col>, ...) | foreign key (<col>) references <Parent>[(<col>)]
|
||
alter table <Table> drop constraint <Name>
|
||
drop_table: "drop table <Name>"
|
||
drop_column: "drop column [from] [table] <Table>: <Name>"
|
||
drop_relationship: |-
|
||
drop relationship <Name>
|
||
drop relationship from <Parent>.<col> to <Child>.<col>
|
||
drop_index: |-
|
||
drop index <Name>
|
||
drop index on <Table> (<col>[, ...])
|
||
drop_constraint: "drop constraint (not null | unique | default | check) from <Table>.<col>"
|
||
add_column: "add column [to] [table] <Table>: <Name> (<Type>)"
|
||
add_relationship: |-
|
||
add 1:n relationship [as <Name>]
|
||
from <Parent>.<col> to <Child>.<col>
|
||
[on delete <action>] [on update <action>]
|
||
[--create-fk]
|
||
add_index: "add index [as <Name>] on <Table> (<col>[, ...])"
|
||
add_constraint: |-
|
||
add constraint not null to <Table>.<col>
|
||
add constraint unique to <Table>.<col>
|
||
add constraint default <value> to <Table>.<col>
|
||
add constraint check (<expr>) to <Table>.<col>
|
||
rename_column: "rename column [in] [table] <Table>: <Old> to <New>"
|
||
change_column: |-
|
||
change column [in] [table] <Table>: <Name> (<Type>)
|
||
[--force-conversion | --dont-convert]
|
||
seed: "seed <Table> [count] [set <col> = ... | in (...) | as <gen> | between x and y] | seed <Table>.<col>"
|
||
show_data: "show data <Table>"
|
||
show_table: "show table <Table>"
|
||
show_tables: "show tables"
|
||
show_relationships: "show relationships"
|
||
show_indexes: "show indexes"
|
||
show_relationship: "show relationship <name>"
|
||
show_index: "show index <name>"
|
||
insert: "insert into <Table> [(<col>[, ...])] [values] (<value>[, ...])"
|
||
update: "update <Table> set <col>=<value>[, ...] (where <col>=<value> | --all-rows)"
|
||
delete: "delete from <Table> (where <col>=<value> | --all-rows)"
|
||
explain: |-
|
||
explain show data <Table> [where <expr>] [limit <n>]
|
||
explain update <Table> set <col>=<value>[, ...] (where <expr> | --all-rows)
|
||
explain delete from <Table> (where <expr> | --all-rows)
|
||
explain <SQL select | with | insert | update | delete> (advanced mode)
|
||
replay: "replay <path> | replay '<path with spaces>'"
|
||
# SQL `SELECT` (advanced mode; ADR-0030 / ADR-0031).
|
||
select: "select (* | <expr>[ as <alias>][, ...]) from <Table> [where <expr>] [order by <expr>[ asc|desc][, ...]] [limit <n>]"
|
||
# SQL `WITH` / CTE (advanced mode; ADR-0032). G4 (ADR-0042):
|
||
# its own template — `with` previously borrowed `select`'s,
|
||
# which never showed the CTE shape.
|
||
with: "with [recursive] <Name> [(<col>[, ...])] as (<query>)[, ...] select ..."
|
||
# App-lifecycle commands (per ADR-0003, surfaced through
|
||
# the parser so they participate in usage templates +
|
||
# completion). Templates here describe the surface
|
||
# grammar that the parser accepts; the in-app `help`
|
||
# listing in `help.in_app_body` carries the user-facing
|
||
# description.
|
||
quit: "quit"
|
||
help: "help [<command>]"
|
||
hint: "hint"
|
||
rebuild: "rebuild"
|
||
save: "save | save as"
|
||
new: "new"
|
||
load: "load"
|
||
export: "export [<path>]"
|
||
import: "import <zip-path> [as <target>]"
|
||
mode: "mode simple | mode advanced"
|
||
messages: "messages | messages short | messages verbose"
|
||
undo: "undo"
|
||
redo: "redo"
|
||
copy: "copy | copy all | copy last"
|
||
|
||
# ---- Pre-submit diagnostics (ADR-0027) -------------------------------
|
||
# Surfaced by the validity indicator and the hint panel before
|
||
# a command is submitted, so a learner sees the problem without
|
||
# having to run the command and read the engine's error.
|
||
diagnostic:
|
||
unknown_table: "no such table: `{name}`"
|
||
unknown_column: "no such column `{name}` on table `{table}`"
|
||
# WARNING-severity flags (ADR-0026 §7) — the command still
|
||
# runs, but the comparison is very likely not intended.
|
||
type_mismatch: "`{column}` is {type} — this comparison uses a value of a different type"
|
||
eq_null: "`= NULL` is never true — use `IS NULL` or `IS NOT NULL`"
|
||
# ADR-0027 Amendment 1: LIKE is a text-pattern match.
|
||
like_numeric: "`{column}` is {type} — `LIKE` is a text-pattern match, not a {type} comparison"
|
||
# Phase-2 diagnostic keys (ADR-0032 §11.5).
|
||
unknown_qualifier: "no such table or alias in scope: `{qualifier}`"
|
||
ambiguous_column: "`{column}` is ambiguous — appears in {qualifiers}"
|
||
projection_alias_misplaced: "alias `{alias}` cannot be used in {clause} — aliases are not bound until after `SELECT`'s projection list"
|
||
# Issue #31: a bare table alias / table name used where the grammar
|
||
# expects a column (e.g. `GROUP BY o`). The name *is* in scope — it
|
||
# is the alias of a FROM source — so calling it an "unknown column"
|
||
# misleads. Point the learner at the qualified `alias.column` form.
|
||
alias_used_as_column: "`{name}` is a table alias — write `{name}.<column>` to reference one of its columns"
|
||
table_used_as_column: "`{name}` is a table — write `{name}.<column>` to reference one of its columns"
|
||
cte_arity_mismatch: "CTE `{cte}` declares {declared} columns but its body has {actual}"
|
||
compound_arity_mismatch: "`{op}` requires both sides to have the same number of columns — left has {left_n}, right has {right_n}"
|
||
duplicate_cte: "duplicate `WITH` table name: `{name}`"
|
||
# ADR-0033 §8 — Phase-3 DML diagnostic keys.
|
||
auto_column_overridden: "column `{column}` is auto-generated (`{type}`); providing an explicit value bypasses the auto-counter and may collide with later auto-generated values"
|
||
insert_arity_mismatch: "the column list names {expected} column(s) but {actual} value(s) are given"
|
||
# ADR-0033 §8.1 / Amendment 5: Form B (no column list) variant
|
||
# (advanced mode — auto-fills nothing, so every column needs a value).
|
||
insert_arity_mismatch_form_b: "with no column list, all {expected} column(s) need a value but {actual} value(s) are given"
|
||
# ADR-0036 Amendment 1 / issue #17: simple-mode Form B. The DSL
|
||
# auto-fills serial/shortid columns, so only the user-fillable columns
|
||
# take values — name both sets so the learner understands the skip.
|
||
insert_arity_mismatch_form_b_simple: "without a column list, supply {expected} value(s) for {columns} — {skipped} auto-generated; {actual} given"
|
||
# ADR-0036 Amendment 1 / issue #17: simple-mode Form B where every
|
||
# column is auto-generated, so the values list takes nothing.
|
||
insert_arity_mismatch_all_auto: "every column of `{table}` is auto-generated, so no values are needed, but {actual} value(s) are given"
|
||
not_null_missing: "column `{column}` is required (`NOT NULL`, no default); the statement will fail when run"
|
||
|
||
# Engine-error translations: an engine-rejected SQL statement
|
||
# reaches the friendly-error layer (ADR-0019) and these keys
|
||
# render its message in engine-neutral wording (ADR-0030 §7).
|
||
# The keys are reached only after a parse-time pass let the
|
||
# query through and the engine rejected at execution.
|
||
engine:
|
||
no_such_table: "no such table: `{name}`"
|
||
no_such_column: "no such column: `{name}`"
|
||
ambiguous_column: "`{column}` is ambiguous — qualify it with a table or alias"
|
||
aggregate_misuse: "`{name}()` cannot be used here — aggregates belong in projection or `HAVING`, not `WHERE`"
|
||
group_by_required: "non-aggregated columns must appear in a `GROUP BY` clause or be wrapped in an aggregate function"
|
||
compound_arity_mismatch: "`{op}` requires both sides to have the same number of columns"
|
||
scalar_subquery_too_many_rows: "scalar subquery returned more than one row — use `IN (…)` or limit the inner query"
|
||
recursive_cte_malformed: "recursive CTE shape is invalid — needs a non-recursive base case combined with `UNION`/`UNION ALL`"
|
||
|
||
# ---- Project lifecycle event notes -----------------------------------
|
||
project:
|
||
rebuild_ok: "[ok] rebuild — {summary}"
|
||
rebuild_failed: "rebuild failed: {error}"
|
||
switched_ok: "[ok] now editing: {display_name}"
|
||
switch_failed: "project switch failed: {error}"
|
||
export_ok: "[ok] export — wrote {path}"
|
||
export_failed: "export failed: {error}"
|
||
# Usage / argument-parsing errors for app-level commands.
|
||
export_usage: "usage: export [<path>]"
|
||
import_usage: "usage: import <zip-path> [as <target>]"
|
||
import_empty_target: "import: empty target after `as`"
|
||
# Project-switch validation failures (load / save-as /
|
||
# import). Returned from the runtime as Err(String) and
|
||
# surfaced via project.switch_failed.
|
||
load_path_missing: "path `{path}` does not exist"
|
||
saveas_target_exists: "`{path}` already exists; pick a different name or remove it first"
|
||
import_zip_missing: "zip `{path}` does not exist"
|
||
# --resume CLI failures printed to stderr before the TUI
|
||
# starts (ADR-0015 §7). Wording stays one line for clean
|
||
# piping; the runtime prepends `rdbms-playground: ` from
|
||
# the calling code itself.
|
||
resume_recorded_missing: "--resume: recorded project `{path}` no longer exists"
|
||
resume_no_previous: "--resume: no previous project recorded under `{data_root}`"
|
||
# Project-lock errors (single-instance enforcement, ADR-0015 §6).
|
||
lock:
|
||
already_held: |-
|
||
project is already open in another rdbms-playground process (pid {pid} on host `{hostname}`); close that process or remove `{path}` if you're sure it's not running
|
||
write: "could not write lock file `{path}`: {source}"
|
||
read: "could not read existing lock file `{path}`: {source}"
|
||
# Temp-project name generation failures.
|
||
naming:
|
||
wordlist_too_small: "wordlist must contain at least 3 entries; found {count}"
|
||
too_many_collisions: |-
|
||
could not generate a non-colliding temp project name after {attempts} attempts
|
||
# User-typed project name validation failures.
|
||
user_name:
|
||
empty: "project name cannot be empty"
|
||
leading_dot: "project name cannot start with `.`"
|
||
invalid_char: "project name cannot contain `{ch}`; use letters, digits, `-`, `_`, or `.` only"
|
||
# ProjectError variants (ProjectError Display path).
|
||
data_root_unavailable: |-
|
||
could not determine the OS-standard data directory; pass --data-dir to override
|
||
path_not_found: "project path `{path}` does not exist"
|
||
not_a_project: |-
|
||
path `{path}` does not look like a project directory (no `project.yaml` and no `playground.db`)
|
||
already_exists: "path `{path}` already exists; pick a different name or remove it first"
|
||
io: "filesystem error at `{path}`: {source}"
|
||
# SafeDeleteError — surfaces in logs when temp-project cleanup
|
||
# refuses to delete a path (ADR-0015 §13).
|
||
safe_delete:
|
||
refused: "refusing to delete `{path}`: {reason}"
|
||
io: "io error on `{path}`: {source}"
|
||
|
||
# ---- DSL failure wrapper + advanced-mode placeholder + fatal --------
|
||
dsl:
|
||
# The friendly-error layer's translated reason, shown beneath the
|
||
# failed command's echo line. ADR-0040: the redundant
|
||
# `"<verb> <subject>" failed:` prefix was dropped — the echo line
|
||
# carries the ✗ marker and names the command.
|
||
failed: "{rendered}"
|
||
# Echo line `running: <input>` shown above each command's
|
||
# response while it executes; ADR-0040 resolves it to
|
||
# `<input> ✓` / `<input> ✗` on completion (the marker replaces
|
||
# the old `[ok] <verb> <subject>` summary line).
|
||
running: "running: {input}"
|
||
|
||
# ---- Value-validation errors (per-column at bind time) --------------
|
||
value:
|
||
type_mismatch: "column `{column}` expects {expected_human}, got {got}"
|
||
format: "column `{column}`: {message}"
|
||
|
||
# ---- Archive / zip errors (export / import) -------------------------
|
||
archive:
|
||
io: "io error on `{path}`: {source}"
|
||
zip: "zip error on `{path}`: {message}"
|
||
export_sequence_exhausted: |-
|
||
could not pick an export filename for `{project}` in `{target_dir}`: all sequence numbers up to {limit} are taken
|
||
import_collision_exhausted: |-
|
||
destination `{path}` already exists and the auto-suffix retries (-02 through -{limit}) are also taken; use `import <zip> as <target>` to choose a different name
|
||
invalid_zip: "zip is malformed: {detail}"
|
||
not_a_project_archive: |-
|
||
zip does not contain a project (no `project.yaml` under a single top-level folder)
|
||
multiple_top_folders: "zip contains more than one top-level folder; refusing to extract"
|
||
unsafe_entry: "zip entry `{entry}` would escape the target directory; refusing to extract"
|
||
|
||
# ---- Persistence-layer errors (CSV/YAML/IO) -------------------------
|
||
# These were thiserror Display attributes pre-round-6. Most surface
|
||
# only as the inner `{message}` of `fatal.persistence` or as the
|
||
# wrapped detail inside `DbError::PersistenceFatal`.
|
||
persistence:
|
||
io: "could not {operation} `{path}`: {source}"
|
||
encode: "could not encode {kind} for `{path}`: {message}"
|
||
csv:
|
||
empty: "CSV is empty"
|
||
invalid_utf8: "invalid UTF-8 in CSV body"
|
||
unterminated_quote: "unterminated quoted field"
|
||
yaml:
|
||
syntax: "project.yaml syntax error: {detail}"
|
||
unsupported_version: "unsupported project.yaml version: {version} (expected 1)"
|
||
unknown_type: "unknown user-facing column type `{raw}` for `{table}.{column}`"
|
||
unknown_action: "unknown referential action `{raw}`"
|
||
migrate:
|
||
version_parse: "could not read version field from project.yaml: {detail}"
|
||
newer_than_supported: |-
|
||
project.yaml is at version {file} but this build only understands up to version {latest}; upgrade the application or restore an older project.yaml
|
||
no_migrator: |-
|
||
no migrator registered for version {version} (programmer error: registry latest_version disagrees with migrators length)
|
||
step_failed: "migrator from v{from} to v{to} failed: {source}"
|
||
bad_output: "migrator produced an unparseable result: {detail}"
|
||
io: "io error during migration on `{path}`: {source}"
|
||
|
||
# ---- Advanced-mode SQL surface (ADR-0030) ---------------------------
|
||
advanced_mode:
|
||
not_implemented: "advanced mode SQL not implemented yet — echo: {input}"
|
||
sql_in_simple: "`{command}` is SQL — available in advanced mode. Switch with `mode advanced`, or prefix the line with `:` to run it once."
|
||
# Appended to a simple-mode DSL error when the same line would run
|
||
# in advanced mode — keeps the actionable DSL fix and adds the mode
|
||
# pointer (ADR-0033 Amendment 3). Suppressed by `advanced_alternative_note`
|
||
# for cases the advanced engine would also reject (issue #1).
|
||
also_valid_sql: "(trying to write SQL? switch with `mode advanced`, or prefix `:` to run once)"
|
||
|
||
# ---- Insert education hints (issue #1 sub-task 2) -------------------
|
||
# Appended to a simple-mode INSERT Form B parse error when the user
|
||
# supplied more values than Form B's non-auto-generated columns expect
|
||
# (but not more than every column). Teaches the contract that excludes
|
||
# `serial` / `shortid` columns and points at the column-list form.
|
||
insert:
|
||
form_b_extra_values_note: "`insert into {table} values (…)` expects {expected_phrase} — {auto_phrase}. To set them explicitly, list every column: `insert into {table} ({all_cols}) values (…)`."
|
||
# Pre-flight teaching note for advanced-mode positional
|
||
# `INSERT INTO T VALUES (…)` (no column list) when the value count
|
||
# doesn't match the column count (issue #1 sub-task 3). The override
|
||
# uses the non-auto columns so the user can omit serial/shortid.
|
||
form_b_positional_count_mismatch_note: "Positional `insert into {table} values (…)` requires a value for every column. `{table}` has {col_count} columns ({col_list}); you supplied {supplied}. To omit auto-generated columns, list the columns you want: `insert into {table} ({non_auto_csv}) values (…)`."
|
||
|
||
# ---- SQL SELECT (advanced mode; ADR-0030 / ADR-0031) ----------------
|
||
select:
|
||
internal_table: "`{table}` is an internal system table and cannot be queried."
|
||
|
||
# ---- Persistence-fatal banner (ADR-0015 §8) -------------------------
|
||
fatal:
|
||
persistence: "FATAL: failed to {operation} `{path}` — {message}. Quitting; investigate and restart."
|
||
|
||
# ---- Modal labels (load picker, rebuild confirm, save-as path) ------
|
||
modal:
|
||
rebuild_cancelled: "rebuild cancelled"
|
||
load_cancelled: "load cancelled"
|
||
generic_cancelled: "{title} cancelled"
|
||
path_entry_empty_name: "path entry: empty name"
|
||
path_entry_empty_path: "path entry: empty path"
|
||
load_picker_nothing: "nothing to load"
|
||
# Modal titles + body prose rendered by ui.rs.
|
||
load_picker_title: "Load project"
|
||
load_picker_empty: "(no projects in data directory)"
|
||
load_picker_path_prompt: "Path to project directory:"
|
||
rebuild_confirm_title: "Rebuild project"
|
||
rebuild_confirm_prompt: "Continue?"
|
||
# Undo / redo confirmation (ADR-0006 Amendment 1).
|
||
undo_confirm_title: "Undo last change"
|
||
redo_confirm_title: "Redo last undone change"
|
||
undo_confirm_command: "This will undo:"
|
||
redo_confirm_command: "This will re-apply:"
|
||
undo_confirm_when: "Snapshot taken {timestamp}"
|
||
undo_confirm_prompt: "Restore that earlier state?"
|
||
undo_cancelled: "undo cancelled"
|
||
redo_cancelled: "redo cancelled"
|
||
|
||
# ---- Undo / redo command surfaces (ADR-0006 Amendment 1) -------------
|
||
undo:
|
||
disabled: "undo is turned off for this session (started with --no-undo)"
|
||
nothing_to_undo: "nothing to undo"
|
||
nothing_to_redo: "nothing to redo"
|
||
undone_ok: "undone: {command}"
|
||
redone_ok: "re-applied: {command}"
|
||
undo_failed: "could not undo: {error}"
|
||
redo_failed: "could not redo: {error}"
|
||
|
||
# ---- Save / save-as command surfaces ---------------------------------
|
||
save:
|
||
# `save` on a named project is a no-op with a friendly hint.
|
||
already_saved: "already auto-saved; use `save as` to copy to a different location"
|
||
# Modal titles for `save` (on a temp) vs `save as`.
|
||
title_as: "Save as"
|
||
title_save: "Save"
|
||
# Prompt body for the path-entry modal opened by save / save as.
|
||
path_prompt: "Name (under data dir/projects) or absolute path:"
|
||
|
||
# ---- Status bar (project label) and panels ---------------------------
|
||
status:
|
||
no_project: "(no project)"
|
||
project_label: "Project: "
|
||
|
||
panel:
|
||
tables_title: "Tables"
|
||
tables_empty: "(none yet)"
|
||
relationships_title: "Relationships"
|
||
relationships_empty: "(none)"
|
||
hint_empty: "Type a command — press Tab for options, `help` for a list"
|
||
# Mode-discovery pointer appended to the empty-input hint in SIMPLE
|
||
# mode (ADR-0051): the `mode advanced` switch left the keybinding
|
||
# strip, so the hint advertises it. Leading separator continues the
|
||
# prompt line. Advanced mode shows no pointer — users know how they
|
||
# got there, and `help` covers the way back.
|
||
hint_mode_advanced: " · `mode advanced` for SQL"
|
||
# Panel titles for the output and hint panels (rendered inside
|
||
# the rounded border, hence the leading/trailing space).
|
||
output_title: "Output"
|
||
hint_title: "Hint"
|
||
|
||
# ---- Shortcut hints (paired with key names in the bottom bar) -------
|
||
# The bottom strip is keystrokes-only and state-aware (ADR-0051). Labels
|
||
# pair with a key name in the renderer (e.g. `Enter` + `run`).
|
||
shortcut:
|
||
confirm: "confirm"
|
||
cancel: "cancel"
|
||
yes: "Yes"
|
||
no: "No"
|
||
load: "load"
|
||
select: "select"
|
||
browse_path: "browse path"
|
||
back_to_list: "back to list"
|
||
# Status-strip labels (ADR-0051, issue #27).
|
||
run: "run"
|
||
nav: "sidebar"
|
||
next_pane: "next pane"
|
||
scroll: "scroll"
|
||
to_input: "input"
|
||
cycle: "cycle"
|
||
browse: "browse"
|
||
clear: "clear"
|
||
complete: "complete"
|
||
history: "history"
|
||
home_end: "home/end"
|
||
del_word: "del word"
|
||
|
||
# ---- mode / messages banners (app-level commands) -------------------
|
||
mode:
|
||
set_simple: "mode: simple"
|
||
set_advanced: "mode: advanced"
|
||
show_simple: "mode: simple"
|
||
show_advanced: "mode: advanced"
|
||
usage: "usage: mode simple | mode advanced"
|
||
unknown: "unknown mode '{value}' (expected 'simple' or 'advanced')"
|
||
# Labels rendered inside the input panel's border to mark the
|
||
# current input mode. `label_advanced_one_shot` is shown
|
||
# while a `:` one-shot is in flight from simple mode.
|
||
label_simple: "SIMPLE"
|
||
label_advanced: "ADVANCED"
|
||
label_advanced_one_shot: "Advanced:"
|
||
|
||
messages:
|
||
show: "messages: {current}"
|
||
set_short: "messages: short"
|
||
set_verbose: "messages: verbose"
|
||
unknown: "unknown messages mode '{value}' (expected 'short' or 'verbose')"
|
||
|
||
# ---- copy (app-level command, ADR-0041) -----------------------------
|
||
copy:
|
||
done: "copied {count} line(s) to the clipboard"
|
||
nothing: "nothing to copy — the output panel is empty"
|
||
unknown: "unknown copy target '{value}' (expected 'all' or 'last')"
|
||
|
||
# ---- Database-error fallback wording + cascade summaries ------------
|
||
db:
|
||
# DbError variants — fallback Display wording for paths that
|
||
# bypass the structured friendly translator (fatal banners,
|
||
# plain to_string() calls). The normal path goes through
|
||
# `friendly::translate_error` which routes by the `kind`
|
||
# field and renders catalog wording from `error.*` instead.
|
||
error:
|
||
sqlite: "database error: {message}"
|
||
unsupported: "operation not supported: {detail}"
|
||
invalid_value: "invalid value: {detail}"
|
||
persistence_fatal: "could not {operation} `{path}`: {message}"
|
||
rebuild_row_failed: |-
|
||
unable to load row {row_number} from `{csv_path}` into table `{table}`: {detail}
|
||
worker_gone: "database worker is no longer available"
|
||
io: "io error: {detail}"
|
||
|
||
cascade:
|
||
# Per-relationship cascade summary appended to a delete
|
||
# success note. The same template handles cascade,
|
||
# set-null, and restrict/no-action cases — `{action}` is
|
||
# one of the three action phrases below.
|
||
summary: " related: {count} row(s) {action} in `{child_table}` for relationship `{rel}` (on delete {on_delete})"
|
||
action_deleted: "deleted"
|
||
action_set_null: "had FK set to null"
|
||
action_blocked: "blocked"
|
||
|
||
# `change column ... (newtype)` dry-run diagnostics (ADR-0017).
|
||
# Surface when the migration would lose information (lossy),
|
||
# produce values the target type can't represent
|
||
# (incompatible), or violate a uniqueness contract (collision).
|
||
diagnostic:
|
||
# Column headers for the diagnostic tables.
|
||
header_from: "From"
|
||
header_to: "To"
|
||
header_reason: "Reason"
|
||
header_value: "Value"
|
||
header_becomes: "Becomes"
|
||
header_source_rows: "Source rows ({pk_label})"
|
||
header_source_values: "Source values"
|
||
# Summary lines printed above each diagnostic table.
|
||
lossy_summary: |-
|
||
Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} row(s) would discard information.
|
||
incompatible_summary: |-
|
||
Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} row(s) cannot be converted.
|
||
uniqueness_summary: |-
|
||
Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} collision(s) would violate uniqueness.
|
||
# Follow-up suggestion appended to the lossy diagnostic
|
||
# (only — incompatibles can't be force-overridden).
|
||
force_conversion_hint: "if you want to execute this conversion in spite of the problems, re-run with `--force-conversion`."
|
||
# `add constraint ...` dry-run refusals (ADR-0029 §5).
|
||
# Surface when the column's existing rows would violate the
|
||
# constraint being added; the offending rows follow in a
|
||
# diagnostic table. There is no force override — the user
|
||
# fixes the data and retries.
|
||
add_not_null_summary: |-
|
||
Cannot add NOT NULL to `{table}.{column}`: {total} row(s) hold a null value.
|
||
add_unique_summary: |-
|
||
Cannot add UNIQUE to `{table}.{column}`: {total} value(s) appear in more than one row.
|
||
add_check_summary: |-
|
||
Cannot add this CHECK to `{table}.{column}`: {total} row(s) do not satisfy `{rule}`.
|
||
|
||
# ---- DSL command success summaries (ADR-0019 §9 sweep) --------------
|
||
# (The DSL → SQL teaching echo's `Executing SQL:` prefix used to live
|
||
# here as `echo.executing_sql`; with the ADR-0038 §4 styled-runs polish
|
||
# the line is now built from a hardcoded constant in `crate::echo`
|
||
# because the dim-prefix + lex-the-rest rendering path in
|
||
# `ui::render_output_line` needs a fixed byte boundary the i18n
|
||
# template couldn't provide. Re-introduce a key here if a non-English
|
||
# locale lands.)
|
||
|
||
# Seed-command notes (ADR-0048): the cap note when the unique-value
|
||
# space is exhausted, and the advisory that flags columns filled with
|
||
# generic text that look like fixed value sets.
|
||
seed:
|
||
capped: "(of {requested} requested — ran out of distinct value combinations)"
|
||
# ADR-0048 D13 (Phase 2/3 wording): name the generically-filled
|
||
# enum-ish / CHECK columns and point at the concrete repairs — the
|
||
# `set` clause on a fresh seed, or the column-fill form for the rows
|
||
# just created.
|
||
advisory_generic: "{columns} filled with generic text — they look like fixed value sets. Pin them next time with `set {column} in ('…', '…')`, or fix these rows with `seed {table}.{column} set {column} in ('…', '…')`."
|
||
|
||
ok:
|
||
# ADR-0040: the generic `[ok] <verb> <subject>` summary line was
|
||
# retired — a successful command's echo line now carries a ✓
|
||
# marker (the verb+subject duplicated the echo above it). The
|
||
# per-operation row-count footers below still convey real payload
|
||
# and are unchanged.
|
||
rows_inserted: " {count} row(s) inserted"
|
||
rows_seeded: " {count} row(s) seeded into {table}"
|
||
rows_updated: " {count} row(s) updated"
|
||
rows_deleted: " {count} row(s) deleted"
|
||
# Shown beneath a `drop column --cascade` summary, once per
|
||
# index removed because it covered the dropped column.
|
||
index_dropped_with_column: " also dropped index `{index}` (it covered the column)"
|
||
|
||
# ---- Client-side success notes (ADR-0017 §6, ADR-0018 §9) ------------
|
||
client_side:
|
||
# Per-cell transformation notice when `change column ...` rewrote
|
||
# at least one stored value (storage-class change or non-identity
|
||
# mapping). `lossy` variant fires when information was discarded —
|
||
# under simple-mode `--force-conversion`, and under advanced-mode
|
||
# `alter table … alter column … type …`, which always converts
|
||
# (ADR-0035 §7).
|
||
transformed: |-
|
||
[client-side] {count} row(s) were transformed before being stored. In raw SQL this would need an explicit `CAST` or application-level code.
|
||
transformed_lossy: |-
|
||
[client-side] {count} row(s) transformed; {lossy} of those discarded information (lossy). In raw SQL this would need an explicit `CAST` or application-level code.
|
||
# Auto-fill notice when null cells were populated by the
|
||
# serial/shortid auto-generation contract (change column path).
|
||
auto_fill_transition: |-
|
||
[client-side] {count} null cell(s) given auto-generated {kind} values. In raw SQL this would need an explicit UPDATE to populate.
|
||
# Auto-fill notice for `add column T: x (serial)` on a
|
||
# non-empty table — values run 1..N.
|
||
auto_fill_add_serial: |-
|
||
[client-side] {count} row(s) given auto-generated serial values 1..{count}. In raw SQL this would need an explicit UPDATE to populate.
|
||
# Auto-fill notice for `add column T: x (shortid)` on a
|
||
# non-empty table.
|
||
auto_fill_add_shortid: |-
|
||
[client-side] {count} row(s) given auto-generated shortid values. In raw SQL this would need an explicit UPDATE to populate.
|
||
# `change column … --dont-convert` caveat (ADR-0038 §6 category 3,
|
||
# Phase 3). The only category-3 *caveat* — the others are illuminating
|
||
# (the headline echo already matches the effect). Here the headline
|
||
# SQL (`ALTER TABLE … SET DATA TYPE …`) *would* convert if run, but
|
||
# `--dont-convert` skipped the client-side layer, so the line above is
|
||
# not equivalent. Fires only in an advanced effective mode (the line
|
||
# references "the line above," i.e. the echo).
|
||
dont_convert_caveat: |-
|
||
[client-side] `--dont-convert` kept the stored values as-is; standard SQL always converts, so running the line above would transform them instead.
|
||
|
||
# ---- Replay command surfaces (ADR-0019 §9 sweep) ---------------------
|
||
replay:
|
||
# Success summary printed when `replay <path>` completes
|
||
# without per-line failure.
|
||
completed: "[ok] replay {path} — {count} command(s) run"
|
||
# File-open failure (line_number == 0 from the runtime).
|
||
failed_open: "replay {path} failed: {error}"
|
||
# Per-line failure header. The command echo is on a
|
||
# follow-up `command_echo` line so the renderer can format
|
||
# them as separate output rows.
|
||
failed_at_line: "replay {path} failed at line {line_number}: {error}"
|
||
# Indented echo of the offending command line, shown after
|
||
# `failed_at_line` when the runtime supplied the source.
|
||
command_echo: " > {command}"
|
||
# Errors the runtime constructs inside ReplayFailed.error
|
||
# before forwarding to App. Carried as plain text so they
|
||
# compose with `failed_at_line`'s `{error}` placeholder.
|
||
error_could_not_open: "could not open `{path}`: {detail}"
|
||
error_parse: "parse error: {detail}"
|
||
# Skipped during replay (ADR-0034): app-lifecycle commands are
|
||
# not re-applied. Most skip silently; `import` and a nested
|
||
# `replay` warn because skipping them can leave the replayed
|
||
# state incomplete (imported data / the nested file's commands
|
||
# are not reconstructed).
|
||
skipped_import: "[skip] line {line}: `{command}` — replay does not re-import; the imported data is not reconstructed"
|
||
skipped_replay: "[skip] line {line}: nested `{command}` — its commands were not replayed"
|