use crate::prelude::*;
use tokio::sync::broadcast;
use crate::logging::{LogLine, LogRingBuffer};
use crate::persist::global::{DatabaseConfig, DatabaseService, WsbRepos};
use crate::state::read_major_version_from_file;
use crate::workspace;
#[derive(Clone)]
pub struct AppContext {
pub db: Arc<WsbRepos>,
pub db_service: Arc<DatabaseService>,
pub workspace_root: PathBuf,
pub project_root: PathBuf,
}
impl AppContext {
pub fn bootstrap(project_root: &Path) -> Result<Self> {
let db_root = workspace::find_super_root_or_project(project_root);
let db_path = db_root.join(".wsb").join("wsb.db");
let config = DatabaseConfig::new(&db_path);
let service = DatabaseService::new(&config)?;
let service = Arc::new(service);
let repos = Arc::new(WsbRepos::new(&service));
let ctx = Self {
db: repos,
db_service: service,
workspace_root: db_root,
project_root: project_root.to_path_buf(),
};
if let Some(major) = read_major_version_from_file(project_root) {
ctx.seed_major_version(major)?;
}
Ok(ctx)
}
pub fn seed_major_version(&self, major: u32) -> Result<()> {
let conn = self.db_service.get_connection()
.context("Failed to get DB connection for major version seeding")?;
let existing: Option<i64> = conn
.query_row(
"SELECT major_version FROM projects LIMIT 1",
[],
|row| row.get(0),
)
.ok();
if existing.is_none() {
conn.execute(
"INSERT OR IGNORE INTO projects (id, code, name, description, status, major_version, version_file, auto_detect_project_files, project_files) \
VALUES ('P001', 'P001', 'Default Project', 'Auto-created project', 'active', ?1, 'version.txt', 1, '[]')",
rusqlite::params![major as i32],
)?;
} else {
let current = existing.unwrap() as u32;
if current == 0 && major > 0 {
conn.execute(
"UPDATE projects SET major_version = ?1 WHERE id = (SELECT id FROM projects LIMIT 1)",
rusqlite::params![major as i32],
)?;
}
}
Ok(())
}
}
#[derive(Clone)]
pub struct AppState {
pub ctx: AppContext,
pub debug: bool,
pub port: u16,
pub log_tx: broadcast::Sender<LogLine>,
pub log_ring: LogRingBuffer,
}
impl AppState {
pub fn new(
ctx: AppContext,
debug: bool,
port: u16,
log_tx: broadcast::Sender<LogLine>,
log_ring: LogRingBuffer,
) -> Self {
Self {
ctx,
debug,
port,
log_tx,
log_ring,
}
}
}
#[derive(Clone, Debug, serde::Serialize)]
pub struct SyncProgress {
pub phase: SyncPhase,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_project: Option<String>,
pub sessions_total: usize,
pub sessions_done: usize,
pub messages_indexed: usize,
}
#[derive(Clone, Debug, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum SyncPhase {
Idle,
Discovering,
Syncing,
Done,
}