use crate::build::build_buildpack_binaries;
use crate::buildpack_kind::{determine_buildpack_kind, BuildpackKind};
use crate::package_descriptor::{normalize_package_descriptor, NormalizePackageDescriptorError};
use crate::{assemble_buildpack_directory, CargoProfile};
use cargo_metadata::MetadataCommand;
use libcnb_common::toml_file::{read_toml_file, write_toml_file, TomlFileError};
use libcnb_data::buildpack::BuildpackId;
use libcnb_data::package_descriptor::PackageDescriptor;
use std::collections::BTreeMap;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
pub fn package_buildpack(
buildpack_directory: &Path,
cargo_profile: CargoProfile,
target_triple: &str,
cargo_build_env: &[(OsString, OsString)],
destination: &Path,
dependencies: &BTreeMap<BuildpackId, PathBuf>,
) -> Result<(), PackageBuildpackError> {
match determine_buildpack_kind(buildpack_directory) {
Some(BuildpackKind::LibCnbRs) => package_libcnb_buildpack(
buildpack_directory,
cargo_profile,
target_triple,
cargo_build_env,
destination,
)
.map_err(PackageBuildpackError::PackageLibcnbBuildpackError),
Some(BuildpackKind::Composite) => {
package_composite_buildpack(buildpack_directory, destination, dependencies)
.map_err(PackageBuildpackError::PackageCompositeBuildpackError)
}
_ => Err(PackageBuildpackError::UnsupportedBuildpack),
}
}
#[derive(thiserror::Error, Debug)]
pub enum PackageBuildpackError {
#[error(transparent)]
PackageCompositeBuildpackError(PackageCompositeBuildpackError),
#[error(transparent)]
PackageLibcnbBuildpackError(PackageLibcnbBuildpackError),
#[error("Buildpack is not supported to be packaged")]
UnsupportedBuildpack,
}
fn package_libcnb_buildpack(
buildpack_directory: &Path,
cargo_profile: CargoProfile,
target_triple: &str,
cargo_build_env: &[(OsString, OsString)],
destination: &Path,
) -> Result<(), PackageLibcnbBuildpackError> {
let cargo_metadata = MetadataCommand::new()
.manifest_path(buildpack_directory.join("Cargo.toml"))
.exec()
.map_err(PackageLibcnbBuildpackError::CargoMetadataError)?;
let buildpack_binaries = build_buildpack_binaries(
buildpack_directory,
&cargo_metadata,
cargo_profile,
cargo_build_env,
target_triple,
)
.map_err(PackageLibcnbBuildpackError::BuildBinariesError)?;
assemble_buildpack_directory(
destination,
buildpack_directory.join("buildpack.toml"),
&buildpack_binaries,
)
.map_err(PackageLibcnbBuildpackError::AssembleBuildpackDirectory)?;
fs::write(
destination.join("package.toml"),
"[buildpack]\nuri = \".\"\n",
)
.map_err(PackageLibcnbBuildpackError::WritePackageDescriptor)
}
#[derive(thiserror::Error, Debug)]
pub enum PackageLibcnbBuildpackError {
#[error("Assembling buildpack directory failed: {0}")]
AssembleBuildpackDirectory(std::io::Error),
#[error("Couldn't write package.toml: {0}")]
WritePackageDescriptor(std::io::Error),
#[error("Building buildpack binaries failed: {0}")]
BuildBinariesError(crate::build::BuildBinariesError),
#[error("Obtaining Cargo metadata failed: {0}")]
CargoMetadataError(cargo_metadata::Error),
}
pub fn package_composite_buildpack(
buildpack_directory: &Path,
destination: &Path,
buildpack_paths: &BTreeMap<BuildpackId, PathBuf>,
) -> Result<(), PackageCompositeBuildpackError> {
fs::copy(
buildpack_directory.join("buildpack.toml"),
destination.join("buildpack.toml"),
)
.map_err(PackageCompositeBuildpackError::CouldNotCopyBuildpackToml)?;
let package_descriptor_path = buildpack_directory.join("package.toml");
let normalized_package_descriptor =
read_toml_file::<PackageDescriptor>(&package_descriptor_path)
.map_err(PackageCompositeBuildpackError::CouldNotReadPackageDescriptor)
.and_then(|package_descriptor| {
normalize_package_descriptor(
&package_descriptor,
&package_descriptor_path,
buildpack_paths,
)
.map_err(PackageCompositeBuildpackError::NormalizePackageDescriptorError)
})?;
write_toml_file(
&normalized_package_descriptor,
destination.join("package.toml"),
)
.map_err(PackageCompositeBuildpackError::CouldNotWritePackageDescriptor)
}
#[derive(thiserror::Error, Debug)]
pub enum PackageCompositeBuildpackError {
#[error("Couldn't copy buildpack.toml: {0}")]
CouldNotCopyBuildpackToml(std::io::Error),
#[error("Couldn't read package.toml: {0}")]
CouldNotReadPackageDescriptor(TomlFileError),
#[error("Error while normalizing package.toml: {0}")]
NormalizePackageDescriptorError(NormalizePackageDescriptorError),
#[error("Couldn't write package.toml: {0}")]
CouldNotWritePackageDescriptor(TomlFileError),
}