libcnb_package/
package_descriptor.rs1use crate::util::absolutize_path;
2use libcnb_data::buildpack::{BuildpackId, BuildpackIdError};
3use libcnb_data::package_descriptor::{
4 PackageDescriptor, PackageDescriptorDependency, PackageDescriptorDependencyError,
5};
6use std::collections::BTreeMap;
7use std::path::{Path, PathBuf};
8
9pub(crate) fn normalize_package_descriptor(
10 descriptor: &PackageDescriptor,
11 descriptor_path: &Path,
12 buildpack_paths: &BTreeMap<BuildpackId, PathBuf>,
13) -> Result<PackageDescriptor, NormalizePackageDescriptorError> {
14 replace_libcnb_uris(descriptor, buildpack_paths)
15 .map_err(NormalizePackageDescriptorError::ReplaceLibcnbUriError)
16 .and_then(|package_descriptor| {
17 absolutize_dependency_paths(&package_descriptor, descriptor_path)
18 .map_err(NormalizePackageDescriptorError::PackageDescriptorDependencyError)
19 })
20}
21
22#[derive(thiserror::Error, Debug)]
23pub enum NormalizePackageDescriptorError {
24 #[error(transparent)]
25 ReplaceLibcnbUriError(ReplaceLibcnbUriError),
26 #[error(transparent)]
27 PackageDescriptorDependencyError(PackageDescriptorDependencyError),
28}
29
30fn replace_libcnb_uris(
31 descriptor: &PackageDescriptor,
32 buildpack_paths: &BTreeMap<BuildpackId, PathBuf>,
33) -> Result<PackageDescriptor, ReplaceLibcnbUriError> {
34 descriptor
35 .dependencies
36 .iter()
37 .map(|dependency| replace_libcnb_uri(dependency, buildpack_paths))
38 .collect::<Result<Vec<_>, _>>()
39 .map(|dependencies| PackageDescriptor {
40 dependencies,
41 ..descriptor.clone()
42 })
43}
44
45fn replace_libcnb_uri(
46 dependency: &PackageDescriptorDependency,
47 buildpack_paths: &BTreeMap<BuildpackId, PathBuf>,
48) -> Result<PackageDescriptorDependency, ReplaceLibcnbUriError> {
49 buildpack_id_from_libcnb_dependency(dependency)
50 .map_err(ReplaceLibcnbUriError::BuildpackIdError)
51 .and_then(|maybe_buildpack_id| {
52 maybe_buildpack_id.map_or(Ok(dependency.clone()), |buildpack_id| {
53 buildpack_paths
54 .get(&buildpack_id)
55 .ok_or(ReplaceLibcnbUriError::MissingBuildpackPath(buildpack_id))
56 .cloned()
57 .and_then(|buildpack_path| {
58 PackageDescriptorDependency::try_from(buildpack_path)
59 .map_err(ReplaceLibcnbUriError::PackageDescriptorDependencyError)
60 })
61 })
62 })
63}
64
65#[derive(thiserror::Error, Debug)]
66pub enum ReplaceLibcnbUriError {
67 #[error("Buildpack reference uses an invalid buildpack id: {0}")]
68 BuildpackIdError(BuildpackIdError),
69 #[error("Invalid package descriptor dependency: {0}")]
70 PackageDescriptorDependencyError(PackageDescriptorDependencyError),
71 #[error("Missing path for buildpack with id {0}")]
72 MissingBuildpackPath(BuildpackId),
73}
74
75fn absolutize_dependency_paths(
76 descriptor: &PackageDescriptor,
77 descriptor_path: &Path,
78) -> Result<PackageDescriptor, PackageDescriptorDependencyError> {
79 let descriptor_parent_path = descriptor_path
80 .parent()
81 .map(PathBuf::from)
82 .unwrap_or_default();
83
84 descriptor
85 .dependencies
86 .iter()
87 .map(|dependency| {
88 let scheme = dependency
89 .uri
90 .scheme()
91 .map(uriparse::scheme::Scheme::as_str);
92
93 match scheme {
94 None => PackageDescriptorDependency::try_from(absolutize_path(
95 &PathBuf::from(dependency.uri.path().to_string()),
96 &descriptor_parent_path,
97 )),
98 _ => Ok(dependency.clone()),
99 }
100 })
101 .collect::<Result<Vec<_>, _>>()
102 .map(|dependencies| PackageDescriptor {
103 dependencies,
104 ..descriptor.clone()
105 })
106}
107
108pub(crate) fn buildpack_id_from_libcnb_dependency(
109 dependency: &PackageDescriptorDependency,
110) -> Result<Option<BuildpackId>, BuildpackIdError> {
111 Some(&dependency.uri)
112 .filter(|uri| {
113 uri.scheme()
114 .is_some_and(|scheme| scheme.as_str() == "libcnb")
115 })
116 .map(|uri| uri.path().to_string().parse())
117 .transpose()
118}