upstream-rs 2.4.0

Fetch package updates directly from the source.
Documentation
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{fs, str::FromStr};

use anyhow::{Result, anyhow, bail};

use crate::services::builder::{
    BuildProfile,
    profiles::{BuildProfileHandler, emit_line_callback, run_command_with_line_callback},
};

pub struct RustProfile;

impl RustProfile {
    fn binary_name(package_name: &str) -> String {
        #[cfg(windows)]
        {
            format!("{package_name}.exe")
        }
        #[cfg(not(windows))]
        {
            package_name.to_string()
        }
    }

    fn find_project_dir(workspace: &Path) -> Option<PathBuf> {
        if workspace.join("Cargo.toml").is_file() {
            Some(workspace.to_path_buf())
        } else {
            None
        }
    }

    fn has_multiple_declared_bins(project_dir: &Path) -> bool {
        let cargo_toml_path = project_dir.join("Cargo.toml");
        let Ok(cargo_toml) = fs::read_to_string(&cargo_toml_path) else {
            return false;
        };

        let Ok(parsed) = toml::Value::from_str(&cargo_toml) else {
            return false;
        };

        parsed
            .get("bin")
            .and_then(toml::Value::as_array)
            .is_some_and(|bins| bins.len() > 1)
    }
}

impl BuildProfileHandler for RustProfile {
    fn profile(&self) -> BuildProfile {
        BuildProfile::Rust
    }

    fn detect(&self, workspace: &Path) -> bool {
        Self::find_project_dir(workspace).is_some()
    }

    fn run_build(
        &self,
        workspace: &Path,
        package_name: &str,
        line_callback: &mut Option<&mut dyn FnMut(&str)>,
    ) -> Result<PathBuf> {
        let project_dir = Self::find_project_dir(workspace).ok_or_else(|| {
            anyhow!(
                "Could not find Cargo.toml in repository root '{}'.",
                workspace.display()
            )
        })?;

        let status = if Self::has_multiple_declared_bins(&project_dir) {
            emit_line_callback(line_callback, "Running cargo build --release --bin ...");
            run_command_with_line_callback(
                Command::new("cargo")
                    .arg("build")
                    .arg("--release")
                    .arg("--bin")
                    .arg(package_name)
                    .current_dir(&project_dir),
                "Failed to run 'cargo build --release --bin <name>'. Is Cargo installed?",
                line_callback,
            )?
        } else {
            emit_line_callback(line_callback, "Running cargo build --release ...");
            run_command_with_line_callback(
                Command::new("cargo")
                    .arg("build")
                    .arg("--release")
                    .current_dir(&project_dir),
                "Failed to run 'cargo build --release'. Is Cargo installed?",
                line_callback,
            )?
        };

        if !status.success() {
            bail!("Cargo build failed for '{}'", package_name);
        }

        let candidate = project_dir
            .join("target")
            .join("release")
            .join(Self::binary_name(package_name));

        if !candidate.exists() {
            return Err(anyhow!(
                "Rust build succeeded but artifact was not found at '{}'",
                candidate.display()
            ));
        }

        Ok(candidate)
    }
}