5c076f6d8f
Every successful user command now persists through to YAML, the
affected CSVs, and history.log inside the same SQLite transaction,
with the commit-db-last ordering from ADR-0015 §6: validate ->
mutate -> stage text + fsync -> atomic rename -> append history ->
commit. A failure in any text-write step rolls back the SQLite tx,
so disk state is unchanged on failure. Persistence failures are
routed through a new AppEvent::PersistenceFatal which sets a
fatal_message on the App, emits Action::Quit, and is printed to
stderr after terminal teardown so the banner remains above the
shell prompt (ADR-0015 §8).
New persistence module owns the file formats: hand-rolled YAML
schema writer, per-type CSV encoder (RFC 4180, NULL distinct from
empty string, base64 blobs), append-only history.log with ISO-8601
timestamps and successful-only entries. Atomic per-file writes via
tmp + fsync + rename.
The db worker holds an Option<Persistence>; tests still use
Database::open(":memory:") with no persistence. Action::ExecuteDsl
gains a source field carrying the user-typed text, threaded
through to history.log.
Tests: 289 passing (256 lib + 7 new integration + 9 lifecycle + 17
walking-skeleton), 0 failing, 0 skipped. Clippy clean with nursery
lints.
59 lines
1.7 KiB
Rust
59 lines
1.7 KiB
Rust
//! Events fed into the application's update function.
|
|
//!
|
|
//! `AppEvent` is the single input type the runtime delivers to
|
|
//! `App::update`. Synthetic instances drive Tier 3 integration
|
|
//! tests (see ADR-0008), so the type is plain data with no
|
|
//! runtime dependency.
|
|
|
|
use crossterm::event::KeyEvent;
|
|
|
|
use crate::db::{DataResult, DeleteResult, InsertResult, TableDescription, UpdateResult};
|
|
use crate::dsl::Command;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum AppEvent {
|
|
Key(KeyEvent),
|
|
Resize {
|
|
cols: u16,
|
|
rows: u16,
|
|
},
|
|
Tick,
|
|
/// A DSL command finished successfully. `description` is
|
|
/// `Some` for commands that produce a table view (create,
|
|
/// add column) and `None` for commands that don't (drop).
|
|
DslSucceeded {
|
|
command: Command,
|
|
description: Option<TableDescription>,
|
|
},
|
|
/// A `show data` query succeeded.
|
|
DslDataSucceeded { command: Command, data: DataResult },
|
|
DslInsertSucceeded {
|
|
command: Command,
|
|
result: InsertResult,
|
|
},
|
|
DslUpdateSucceeded {
|
|
command: Command,
|
|
result: UpdateResult,
|
|
},
|
|
DslDeleteSucceeded {
|
|
command: Command,
|
|
result: DeleteResult,
|
|
},
|
|
/// A DSL command failed. `error` is already a friendly
|
|
/// message produced via `DbError::friendly_message`.
|
|
DslFailed {
|
|
command: Command,
|
|
error: String,
|
|
},
|
|
/// Refreshed list of tables in the database.
|
|
TablesRefreshed(Vec<String>),
|
|
/// A persistence failure occurred (ADR-0015 §8). The
|
|
/// application surfaces a fatal banner and exits cleanly so
|
|
/// the message remains above the shell prompt.
|
|
PersistenceFatal {
|
|
operation: String,
|
|
path: std::path::PathBuf,
|
|
message: String,
|
|
},
|
|
}
|