vorpal-sdk 0.2.0

Rust SDK for building Vorpal artifacts.
Documentation
use crate::{
    api::artifact::{
        ArtifactSystem,
        ArtifactSystem::{Aarch64Darwin, Aarch64Linux, X8664Darwin, X8664Linux},
    },
    artifact::{
        cargo::Cargo, clippy::Clippy, get_env_key, rust_analyzer::RustAnalyzer, rust_src::RustSrc,
        rust_std::RustStd, rustc::Rustc, rustfmt::Rustfmt, step, Artifact,
    },
    context::ConfigContext,
};
use anyhow::{bail, Result};
use indoc::formatdoc;

pub fn target(system: ArtifactSystem) -> Result<String> {
    let target = match system {
        Aarch64Darwin => "aarch64-apple-darwin",
        Aarch64Linux => "aarch64-unknown-linux-gnu",
        X8664Darwin => "x86_64-apple-darwin",
        X8664Linux => "x86_64-unknown-linux-gnu",
        _ => bail!("unsupported 'rust-toolchain' system: {:?}", system),
    };

    Ok(target.to_string())
}

pub fn version() -> String {
    "1.93.1".to_string()
}

#[derive(Default)]
pub struct RustToolchain<'a> {
    cargo: Option<&'a str>,
    clippy: Option<&'a str>,
    rust_analyzer: Option<&'a str>,
    rust_src: Option<&'a str>,
    rust_std: Option<&'a str>,
    rustc: Option<&'a str>,
    rustfmt: Option<&'a str>,
}

impl<'a> RustToolchain<'a> {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn with_cargo(mut self, cargo: &'a str) -> Self {
        self.cargo = Some(cargo);
        self
    }

    pub fn with_clippy(mut self, clippy: &'a str) -> Self {
        self.clippy = Some(clippy);
        self
    }

    pub fn with_rust_analyzer(mut self, rust_analyzer: &'a str) -> Self {
        self.rust_analyzer = Some(rust_analyzer);
        self
    }

    pub fn with_rust_src(mut self, rust_src: &'a str) -> Self {
        self.rust_src = Some(rust_src);
        self
    }

    pub fn with_rust_std(mut self, rust_std: &'a str) -> Self {
        self.rust_std = Some(rust_std);
        self
    }

    pub fn with_rustc(mut self, rustc: &'a str) -> Self {
        self.rustc = Some(rustc);
        self
    }

    pub fn with_rustfmt(mut self, rustfmt: &'a str) -> Self {
        self.rustfmt = Some(rustfmt);
        self
    }

    pub async fn build(self, context: &mut ConfigContext) -> Result<String> {
        let cargo = match self.cargo {
            Some(digest) => digest.to_string(),
            None => Cargo::new().build(context).await?,
        };

        let clippy = match self.clippy {
            Some(digest) => digest.to_string(),
            None => Clippy::new().build(context).await?,
        };

        let rust_analyzer = match self.rust_analyzer {
            Some(digest) => digest.to_string(),
            None => RustAnalyzer::new().build(context).await?,
        };

        let rust_src = match self.rust_src {
            Some(digest) => digest.to_string(),
            None => RustSrc::new().build(context).await?,
        };

        let rust_std = match self.rust_std {
            Some(digest) => digest.to_string(),
            None => RustStd::new().build(context).await?,
        };

        let rustc = match self.rustc {
            Some(digest) => digest.to_string(),
            None => Rustc::new().build(context).await?,
        };

        let rustfmt = match self.rustfmt {
            Some(digest) => digest.to_string(),
            None => Rustfmt::new().build(context).await?,
        };

        let artifacts = vec![
            cargo,
            clippy,
            rust_analyzer,
            rust_src,
            rust_std,
            rustc,
            rustfmt,
        ];

        let mut toolchain_component_paths = vec![];

        for component in &artifacts {
            toolchain_component_paths.push(get_env_key(component));
        }

        let toolchain_target = target(context.get_system())?;
        let toolchain_version = version();

        let step_script = formatdoc! {"
            toolchain_dir=\"$VORPAL_OUTPUT/toolchains/{toolchain_version}-{toolchain_target}\"

            mkdir -p \"$toolchain_dir\"

            components=({component_paths})

            echo \"Copying Rust toolchain components to $toolchain_dir...\"

            for component in \"${{components[@]}}\"; do
                echo \"Processing component: $component\"

                find \"$component\" | while read -r file; do
                    relative_path=$(echo \"$file\" | sed -e \"s|$component||\")

                    if [[ \"$relative_path\" == \"/manifest.in\" ]]; then
                        continue
                    fi

                    if [ -d \"$file\" ]; then
                        mkdir -p \"$toolchain_dir$relative_path\"
                    else
                        cp -p \"$file\" \"$toolchain_dir$relative_path\"
                    fi
                done
            done

            cat > \"$VORPAL_OUTPUT/settings.toml\" << \"EOF\"
            auto_self_update = \"disable\"
            profile = \"minimal\"
            version = \"12\"

            [overrides]
            EOF",
            component_paths = toolchain_component_paths.join(" "),
        };

        let steps = vec![step::shell(context, artifacts, vec![], step_script, vec![]).await?];
        let systems = vec![Aarch64Darwin, Aarch64Linux, X8664Darwin, X8664Linux];
        let name = "rust-toolchain";

        Artifact::new(name, steps, systems)
            .with_aliases(vec![format!("{name}:{toolchain_version}")])
            .build(context)
            .await
    }
}