DSL parser, async DB worker, types, history, metadata, polish
Track 1 implementation plus polish round. Parser (chumsky): - Grammar-based DSL producing a typed Command AST. - create table X with pk [name:type[,name:type...]] supports arbitrary names, any user type, compound PKs natively. Bare form errors with a friendly hint pointing at `with pk`. - add column to table X: Name (type); drop table X. - Required clauses use keyword grammar; -- reserved for opt-in flags (ADR-0009). Custom Rich reasons preferred when surfacing chumsky errors so unknown-type messages list valid alternatives. Database (ADR-0010, ADR-0012): - rusqlite + STRICT tables + foreign_keys=ON. - Dedicated worker thread; mpsc Request inbox, oneshot replies. - Typed DbError with friendly_message() hook for H1. - Internal __rdbms_playground_columns metadata table preserves user-facing types across schema reads, atomically maintained alongside DDL via Connection transactions. list_tables hides it via the new __rdbms_ internal-table convention. Types (ADR-0005, ADR-0011): - All ten user-facing types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid. - Type::fk_target_type() for FK-side column-type rule (Serial->Int, ShortId->Text, others identity) -- foundation for the FK iteration. App / Runtime / UI: - update() stays pure-sync; runtime dispatches DSL via spawned tasks, results post back as AppEvent::Dsl*. - Items panel renders live tables list; output panel shows the user-facing structure of the current table after each DDL. - In-memory command history (Up/Down, draft preservation, consecutive-duplicate dedup) -- I2 partial. - Mouse capture removed; terminal native text selection restored (toggle approach revisited when scroll/click features land). Docs: - ADRs 0009 (DSL syntax conventions), 0010 (DB worker), 0011 (FK type compat), 0012 (internal metadata table). - requirements.md progress notes; new V4 entry for the scrollable session-log + inline rich rendering + Markdown export direction. Tests: 103 passing (91 lib + 12 integration), 0 skipped. Clippy clean with nursery enabled.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
//! The Command AST.
|
||||
//!
|
||||
//! `Command` is the parser's output and the database worker's
|
||||
//! input. Each variant carries fully validated data — the parser
|
||||
//! is responsible for shape, the database worker for semantics
|
||||
//! (e.g. "table does not exist").
|
||||
//!
|
||||
//! The shape supports compound primary keys natively even though
|
||||
//! only the dedicated `with pk a:int,b:int` grammar exposes them
|
||||
//! today. Future grammar extensions (inline column specs, `set
|
||||
//! primary key`, junction-table convenience commands) emit into
|
||||
//! the same shape.
|
||||
|
||||
use crate::dsl::types::Type;
|
||||
|
||||
/// A column at table-creation time: a name and a user-facing
|
||||
/// type. Constraints beyond `PRIMARY KEY` (NOT NULL, UNIQUE,
|
||||
/// CHECK, DEFAULT) come in later iterations.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ColumnSpec {
|
||||
pub name: String,
|
||||
pub ty: Type,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Command {
|
||||
CreateTable {
|
||||
name: String,
|
||||
/// Columns to create, in declaration order.
|
||||
columns: Vec<ColumnSpec>,
|
||||
/// Names of columns forming the primary key. Length 1 is
|
||||
/// a single PK; length >= 2 is a compound PK; length 0
|
||||
/// indicates no primary key (a future grammar option,
|
||||
/// not produced by today's parser).
|
||||
primary_key: Vec<String>,
|
||||
},
|
||||
DropTable {
|
||||
name: String,
|
||||
},
|
||||
AddColumn {
|
||||
table: String,
|
||||
column: String,
|
||||
ty: Type,
|
||||
},
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Short label for log output and result rendering.
|
||||
#[must_use]
|
||||
pub const fn verb(&self) -> &'static str {
|
||||
match self {
|
||||
Self::CreateTable { .. } => "create table",
|
||||
Self::DropTable { .. } => "drop table",
|
||||
Self::AddColumn { .. } => "add column",
|
||||
}
|
||||
}
|
||||
|
||||
/// The table this command targets — every Command in this
|
||||
/// iteration operates on exactly one table.
|
||||
#[must_use]
|
||||
pub fn target_table(&self) -> &str {
|
||||
match self {
|
||||
Self::CreateTable { name, .. } | Self::DropTable { name } => name,
|
||||
Self::AddColumn { table, .. } => table,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user