wsb 0.275.7622

Workspace tools for every project under $PRJ: git across submodules, template stamping, GitHub, and the workbench
Documentation
use crate::prelude::*;

use crate::local_state::LocalState;
use crate::persist::global::{DatabaseService, WsbRepos};

/// What a command that works on a worktree has open: where it runs, its
/// local state, and its rows of the workshop's database.
#[derive(Clone)]
pub struct AppContext {
    pub db: Arc<WsbRepos>,
    pub db_service: Arc<DatabaseService>,
    /// The worktree's local state: its folder under `$PRJ/.state`, and the
    /// key its rows of the database carry.
    pub state: LocalState,
    /// The worktree's root: the checkout of the project's main repo, the
    /// outermost repository above where the command runs.
    pub worktree_root: PathBuf,
    /// The repository wsb manages that the command runs in: the worktree
    /// root, or a submodule of it.
    pub repo_root: PathBuf,
}

impl AppContext {
    /// Open the worktree that the managed repository at `repo_root` is in,
    /// and the workshop's database.
    ///
    /// A worktree an older wsb left half in its layout is refused: what it
    /// holds there would be read by one wsb and not the other.
    pub fn bootstrap(repo_root: &Path) -> Result<Self> {
        Self::bootstrap_in(repo_root, crate::store::open()?)
    }

    /// The same, in a database already open: the daemon's, which it opened
    /// where it was told to.
    pub fn bootstrap_in(repo_root: &Path, service: Arc<DatabaseService>) -> Result<Self> {
        let worktree_root = crate::roots::find_super_root(repo_root).unwrap_or_else(|| repo_root.to_path_buf());
        let leftovers = crate::migrate::leftovers_in(&worktree_root)?;
        if !leftovers.is_empty() {
            anyhow::bail!(
                "{} still holds an older wsb's layout:\n  {}\nRun `wsb migrate` to bring it across",
                worktree_root.display(),
                leftovers.join("\n  ")
            );
        }
        let state = LocalState::of(&worktree_root)?;
        Ok(Self {
            db: Arc::new(WsbRepos::new(&service, state.key())),
            db_service: service,
            state,
            worktree_root,
            repo_root: repo_root.to_path_buf(),
        })
    }
}

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