Iteration 1: file-backed projects with auto-named temps, lock file, and L1 CLI
Replaces the in-memory database with an on-disk project. Startup either opens a project at the positional CLI path (L1) or creates an auto-named temp project (<YYYYMMDD>-<word>-<word>-<word>) under the OS-standard data directory or a --data-dir override. The new project::Project type owns the directory skeleton and a PID+hostname lock file with stale-lock takeover via sysinfo. The status bar now shows "Project: <Display Name>", derived by a small kebab/snake/camel prettifier. Per-command persistence to YAML/CSV/history.log is NOT yet wired -- that's Iteration 2; for now playground.db carries the state across quits. Tests: 257 passing (231 lib + 9 new integration + 17 existing), 0 failing, 0 skipped. Clippy clean with nursery lints.
This commit is contained in:
+15
-6
@@ -26,11 +26,13 @@ use tracing::{debug, error, info, warn};
|
||||
|
||||
use crate::action::Action;
|
||||
use crate::app::App;
|
||||
use crate::cli::Args;
|
||||
use crate::db::{
|
||||
DataResult, Database, DbError, DeleteResult, InsertResult, TableDescription, UpdateResult,
|
||||
};
|
||||
use crate::dsl::Command;
|
||||
use crate::event::AppEvent;
|
||||
use crate::project::open_or_create;
|
||||
use crate::theme::Theme;
|
||||
use crate::ui;
|
||||
|
||||
@@ -39,17 +41,22 @@ const SHUTDOWN_GRACE: Duration = Duration::from_millis(100);
|
||||
|
||||
/// Run the application until a `Quit` action is enacted or the
|
||||
/// terminal closes.
|
||||
pub async fn run(theme: Theme) -> Result<()> {
|
||||
// For this iteration, every session uses a fresh in-memory
|
||||
// database. Track 2 (project storage) wires up file-backed
|
||||
// databases with proper lifecycle management.
|
||||
let database = Database::open(":memory:").context("open database")?;
|
||||
pub async fn run(args: Args) -> Result<()> {
|
||||
let project = open_or_create(args.project_path.as_deref(), args.data_dir.as_deref())
|
||||
.context("open or create project")?;
|
||||
let db_path = project.db_path();
|
||||
let display_name = project.display_name().to_string();
|
||||
let database = Database::open(db_path.as_path()).context("open database")?;
|
||||
|
||||
let mut terminal = setup_terminal().context("setup terminal")?;
|
||||
let result = run_loop(&mut terminal, theme, database).await;
|
||||
let result = run_loop(&mut terminal, args.theme, database, display_name).await;
|
||||
if let Err(e) = teardown_terminal(&mut terminal) {
|
||||
// Teardown failures should not mask the primary error.
|
||||
warn!(error = %e, "terminal teardown failed");
|
||||
}
|
||||
// `project` (and the lock it holds) is dropped here, releasing
|
||||
// the lock file *after* the terminal has been restored.
|
||||
drop(project);
|
||||
result
|
||||
}
|
||||
|
||||
@@ -57,11 +64,13 @@ async fn run_loop(
|
||||
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
|
||||
theme: Theme,
|
||||
database: Database,
|
||||
project_display_name: String,
|
||||
) -> Result<()> {
|
||||
let (event_tx, mut event_rx) = mpsc::channel::<AppEvent>(EVENT_CHANNEL_CAPACITY);
|
||||
let reader_handle = spawn_event_reader(event_tx.clone());
|
||||
|
||||
let mut app = App::new();
|
||||
app.project_name = Some(project_display_name);
|
||||
|
||||
// Seed the table list with whatever the database currently
|
||||
// shows. For a fresh in-memory DB this is empty, but doing
|
||||
|
||||
Reference in New Issue
Block a user