wsb 0.215.4579

Workspace - A tool suite for file operations, version management, and development workflow automation
Documentation
use crate::fleet::error::Result;
use crate::fleet::git::{GitOps, SubmoduleOps, SubmoduleStatus, SubmoduleTree};
use crate::fleet::output::Formatter;
use crate::prelude::*;
use rayon::prelude::*;

pub fn execute<P: AsRef<Path> + Sync>(repo_root: P, _verbose: bool) -> Result<()> {
    // Reporting status is meaningful even when nothing is left to report on —
    // the superproject's own state still matters, and an all-ignored tree is a
    // configured outcome rather than an error.
    let tree = SubmoduleTree::discover_allow_empty(&repo_root)?;

    let max_depth = tree
        .top_down()
        .iter()
        .map(|node| node.depth + 1)
        .max()
        .unwrap_or(0);
    let ignored = tree.ignored().len();
    Formatter::print_info(&format!(
        "Superproject + {} submodule(s) across {} level(s){}",
        tree.len(),
        max_depth,
        if ignored == 0 {
            String::new()
        } else {
            format!(" ({ignored} ignored)")
        }
    ));

    // Get status for superproject
    let repo = GitOps::open_repo(&repo_root)?;
    let is_detached = GitOps::is_detached_head(&repo)?;
    let current_branch = if is_detached {
        None
    } else {
        Some(GitOps::current_branch(&repo)?)
    };
    let is_clean = GitOps::is_clean(&repo)?;
    let uncommitted_files = if !is_clean {
        GitOps::get_uncommitted_files(&repo)?
    } else {
        Vec::new()
    };
    let (ahead, behind) = if !is_detached {
        GitOps::get_ahead_behind(&repo).unwrap_or((0, 0))
    } else {
        (0, 0)
    };

    let super_status = SubmoduleStatus {
        name: "[SUPERPROJECT]".to_string(),
        path: ".".to_string(),
        current_branch,
        is_clean,
        is_detached,
        ahead,
        behind,
        uncommitted_files,
        exists: true,
        depth: 0,
    };

    // Status for every submodule at every depth, in parallel. Discovery order is
    // preserved so children stay under their parent in the output.
    let mut statuses: Vec<_> = tree
        .top_down()
        .par_iter()
        .map(SubmoduleOps::get_status)
        .collect::<Result<Vec<_>>>()?;

    // Prepend superproject status
    statuses.insert(0, super_status);

    Formatter::print_status(&statuses);

    Ok(())
}