tracel-xtask 4.18.2

Reusable and Extensible xtask commands to manage repositories.
Documentation
use anyhow::Ok;
use strum::IntoEnumIterator;
use tracel_xtask_utils::{
    endgroup,
    environment::Environment,
    group,
    process::{run_process_for_package, run_process_for_workspace},
    workspace::{WorkspaceMember, WorkspaceMemberType, get_workspace_members},
};

use crate::{commands::WARN_IGNORED_ONLY_ARGS, context::Context};

use super::Target;

#[tracel_xtask_macros::declare_command_args(Target, DocSubCommand)]
pub struct DocCmdArgs {}

pub fn handle_command(args: DocCmdArgs, _env: Environment, _ctx: Context) -> anyhow::Result<()> {
    if args.target == Target::Workspace && !args.only.is_empty() {
        log::warn!("{WARN_IGNORED_ONLY_ARGS}");
    }
    match args.get_command() {
        DocSubCommand::Build => run_documentation_build(
            &args.target,
            &args.exclude,
            &args.only,
            &args.features,
            args.no_default_features,
        ),
        DocSubCommand::Tests => run_documentation(
            &args.target,
            &args.exclude,
            &args.only,
            &args.features,
            args.no_default_features,
        ),
    }
}

fn run_documentation_build(
    target: &Target,
    excluded: &Vec<String>,
    only: &Vec<String>,
    features: &[String],
    no_default_features: bool,
) -> anyhow::Result<()> {
    match target {
        Target::Workspace => {
            group!("Build Workspace documentation");
            let mut cmd_args = vec!["doc", "--workspace", "--no-deps", "--color=always"];

            if no_default_features {
                cmd_args.push("--no-default-features");
            }

            let features_str = features.join(",");
            if !features.is_empty() {
                cmd_args.push("--features");
                cmd_args.push(&features_str);
            }

            run_process_for_workspace(
                "cargo",
                &cmd_args,
                None,
                excluded,
                None,
                None,
                "Workspace documentation build failed",
                None,
                None,
            )?;
            endgroup!();
        }
        Target::Crates | Target::Examples => {
            let members = match target {
                Target::Crates => get_workspace_members(WorkspaceMemberType::Crate),
                Target::Examples => get_workspace_members(WorkspaceMemberType::Example),
                _ => unreachable!(),
            };

            for member in members {
                group!("Doc Build: {}", member.name);
                let mut cmd_args = vec!["doc", "-p", &member.name, "--no-deps", "--color=always"];

                if no_default_features {
                    cmd_args.push("--no-default-features");
                }

                let features_str = features.join(",");
                if !features.is_empty() {
                    cmd_args.push("--features");
                    cmd_args.push(&features_str);
                }

                run_process_for_package(
                    "cargo",
                    &member.name,
                    &cmd_args,
                    None,
                    excluded,
                    only,
                    &format!("Format check execution failed for {}", &member.name),
                    None,
                    None,
                )?;
                endgroup!();
            }
        }
        Target::AllPackages => {
            Target::iter()
                .filter(|t| *t != Target::AllPackages && *t != Target::Workspace)
                .try_for_each(|t| {
                    run_documentation_build(&t, excluded, only, features, no_default_features)
                })?;
        }
    }
    Ok(())
}

pub(crate) fn run_documentation(
    target: &Target,
    excluded: &[String],
    only: &[String],
    features: &[String],
    no_default_features: bool,
) -> anyhow::Result<()> {
    match target {
        Target::Workspace => {
            group!("Workspace Documentation Tests");
            let mut cmd_args = vec!["test", "--workspace", "--doc", "--color", "always"];

            if no_default_features {
                cmd_args.push("--no-default-features");
            }

            let features_str = features.join(",");
            if !features.is_empty() {
                cmd_args.push("--features");
                cmd_args.push(&features_str);
            }

            run_process_for_workspace(
                "cargo",
                &cmd_args,
                None,
                excluded,
                Some(r"Doc-tests (\w+)"),
                Some("Doc Tests"),
                "Workspace documentation test failed",
                None,
                None,
            )?;
            endgroup!();
        }
        Target::Crates | Target::Examples => {
            let members = match target {
                Target::Crates => get_workspace_members(WorkspaceMemberType::Crate),
                Target::Examples => get_workspace_members(WorkspaceMemberType::Example),
                _ => unreachable!(),
            };

            for member in members {
                run_doc_test(&member, excluded, only, features, no_default_features)?;
            }
        }
        Target::AllPackages => {
            Target::iter()
                .filter(|t| *t != Target::AllPackages && *t != Target::Workspace)
                .try_for_each(|t| {
                    run_documentation(&t, excluded, only, features, no_default_features)
                })?;
        }
    }
    Ok(())
}

fn run_doc_test(
    member: &WorkspaceMember,
    excluded: &[String],
    only: &[String],
    features: &[String],
    no_default_features: bool,
) -> Result<(), anyhow::Error> {
    group!("Doc Tests: {}", member.name);
    let mut cmd_args = vec!["test", "--doc", "-p", &member.name];

    if no_default_features {
        cmd_args.push("--no-default-features");
    }

    let features_str = features.join(",");
    if !features.is_empty() {
        cmd_args.push("--features");
        cmd_args.push(&features_str);
    }
    run_process_for_package(
        "cargo",
        &member.name,
        &cmd_args,
        None,
        excluded,
        only,
        &format!(
            "Failed to execute documentation test for '{}'",
            &member.name
        ),
        Some("no library targets found"),
        Some(&format!(
            "No library found to test documentation for in the crate '{}'",
            &member.name
        )),
    )?;
    endgroup!();
    Ok(())
}