wsb 0.93.391653

Workspace - A tool suite for file operations, version management, and development workflow automation
Documentation
use anyhow::Result;
use std::sync::Arc;
use tokio::sync::{broadcast, Mutex};

use super::db::SessionDb;
use super::github::GitHubClient;
use super::sync::SyncEngine;

/// Shared application state for all request handlers.
#[derive(Clone)]
pub struct WebState {
    pub db: SessionDb,
    pub sync_engine: Arc<Mutex<SyncEngine>>,
    pub github: Arc<Mutex<Option<GitHubClient>>>,
    pub sync_progress_tx: broadcast::Sender<SyncProgress>,
    pub debug: bool,
    pub port: u16,
}

impl WebState {
    pub async fn new(debug: bool, port: u16) -> Result<Self> {
        let db = SessionDb::open().await?;
        let mut sync_engine = SyncEngine::new(db.clone());
        sync_engine.load_skip_cache().await?;
        let (sync_progress_tx, _) = broadcast::channel(256);

        // Try to load GitHub token from config
        let github = match load_github_token() {
            Some(token) => Some(GitHubClient::new(&token)),
            None => None,
        };

        Ok(Self {
            db,
            sync_engine: Arc::new(Mutex::new(sync_engine)),
            github: Arc::new(Mutex::new(github)),
            sync_progress_tx,
            debug,
            port,
        })
    }
}

/// Progress update emitted during sync operations.
#[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,
}

/// Check if a GitHub token is configured.
pub fn load_github_token_exists() -> bool {
    load_github_token().is_some()
}

/// Load GitHub token from ~/.wsb/config.toml
pub fn load_github_token() -> Option<String> {
    let config_path = dirs::home_dir()?.join(".wsb").join("config.toml");
    let content = std::fs::read_to_string(&config_path).ok()?;
    let table: toml::Table = content.parse().ok()?;
    let github = table.get("github")?.as_table()?;
    let token = github.get("token")?.as_str()?;
    if token.is_empty() {
        None
    } else {
        Some(token.to_string())
    }
}