wsb-daemon 0.258.6441

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! What each machine has, and which of it is shared.
//!
//! An inventory is a list of workspace IDENTITIES, never of paths. Two
//! machines keep the same workspace in different directories, and the whole
//! point of deriving the id from the repository is that neither has to be told
//! what the other calls it.

use std::collections::BTreeMap;

use wsb::peer::model::{WorkspaceId, WorkspacePresence};
use wsb::peer::protocol::WorkspaceSummary;

/// One workspace, as the two machines together see it.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SharedWorkspace {
    pub id: WorkspaceId,
    /// What this machine calls it, or what the peer does when only the peer
    /// has it.
    pub display_name: String,
    pub presence: WorkspacePresence,
    /// Whether the two sides agree about which repository this workspace is.
    ///
    /// Two workspaces can only collide here by holding the same superproject
    /// root commit, which means they ARE the same history — but a restored
    /// backup or a hand-edited database could produce a mismatch, and pairing
    /// two unrelated trees is the one thing worth checking twice.
    pub compatible: bool,
}

/// Compare two inventories.
pub fn compare(mine: &[WorkspaceSummary], theirs: &[WorkspaceSummary]) -> Vec<SharedWorkspace> {
    let theirs_by_id: BTreeMap<&WorkspaceId, &WorkspaceSummary> =
        theirs.iter().map(|summary| (&summary.workspace, summary)).collect();
    let mut out = Vec::new();

    for summary in mine {
        match theirs_by_id.get(&summary.workspace) {
            Some(peer) => out.push(SharedWorkspace {
                id: summary.workspace.clone(),
                display_name: summary.display_name.clone(),
                presence: WorkspacePresence::Shared,
                compatible: peer.root_commit == summary.root_commit,
            }),
            None => out.push(SharedWorkspace {
                id: summary.workspace.clone(),
                display_name: summary.display_name.clone(),
                presence: WorkspacePresence::LocalOnly,
                compatible: true,
            }),
        }
    }

    let mine_ids: std::collections::BTreeSet<&WorkspaceId> =
        mine.iter().map(|summary| &summary.workspace).collect();
    for summary in theirs {
        if !mine_ids.contains(&summary.workspace) {
            out.push(SharedWorkspace {
                id: summary.workspace.clone(),
                display_name: summary.display_name.clone(),
                presence: WorkspacePresence::PeerOnly,
                compatible: true,
            });
        }
    }

    out.sort_by(|a, b| a.display_name.cmp(&b.display_name).then(a.id.cmp(&b.id)));
    out
}

#[cfg(test)]
#[path = "inventory_tests.rs"]
mod tests;