wsb 0.181.4020

Workspace - A tool suite for file operations, version management, and development workflow automation
Documentation
use crate::prelude::*;
use tokio::sync::{broadcast, RwLock};

use super::home_dir;
use super::github::GitHubClient;
use super::handlers::fleet::RepoDetailedStatus;
use super::ops::OpsRegistry;
use super::sync::SyncHandle;
use crate::persist::global::{DatabaseService, WsbRepos};
use crate::logging::{LogLine, LogRingBuffer};

#[derive(Clone)]
pub struct WebState {
    pub db: Arc<WsbRepos>,
    pub db_service: Arc<DatabaseService>,
    pub sync: SyncHandle,
    pub github: Arc<RwLock<Option<GitHubClient>>>,
    pub sync_progress_tx: broadcast::Sender<SyncProgress>,
    pub log_tx: broadcast::Sender<LogLine>,
    pub log_ring: LogRingBuffer,
    pub workspace_root: PathBuf,
    pub debug: bool,
    pub port: u16,
    pub git_status_tx: broadcast::Sender<RepoDetailedStatus>,
    pub git_status_snapshot: Arc<RwLock<HashMap<String, RepoDetailedStatus>>>,
    pub git_focus_tx: broadcast::Sender<String>,
    pub git_nudge_tx: broadcast::Sender<Option<String>>,
    pub ops: OpsRegistry,
}

impl WebState {
    pub async fn new(
        db: Arc<WsbRepos>,
        db_service: Arc<DatabaseService>,
        workspace_root: PathBuf,
        debug: bool,
        port: u16,
        log_tx: broadcast::Sender<LogLine>,
        log_ring: LogRingBuffer,
    ) -> Result<Self> {
        let sync = super::sync::spawn_sync_actor(db.clone());

        let (sync_progress_tx, _) = broadcast::channel(256);

        let github = match load_github_token() {
            Some(token) => Some(GitHubClient::new(&token)),
            None => None,
        };

        let (git_status_tx, _) = broadcast::channel(64);
        let (git_focus_tx, _) = broadcast::channel(16);
        let (git_nudge_tx, _) = broadcast::channel::<Option<String>>(64);

        Ok(Self {
            db,
            db_service,
            sync,
            github: Arc::new(RwLock::new(github)),
            sync_progress_tx,
            log_tx,
            log_ring,
            workspace_root,
            debug,
            port,
            git_status_tx,
            git_status_snapshot: Arc::new(RwLock::new(HashMap::new())),
            git_focus_tx,
            git_nudge_tx,
            ops: OpsRegistry::new(),
        })
    }
}

#[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,
}

pub fn load_github_token_exists() -> bool {
    load_github_token().is_some()
}

pub fn load_github_token() -> Option<String> {
    let config_path = home_dir().ok()?.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())
    }
}