libcnb_package/
buildpack_dependency_graph.rs1use crate::buildpack_kind::BuildpackKind;
2use crate::buildpack_kind::determine_buildpack_kind;
3use crate::dependency_graph::{
4 CreateDependencyGraphError, DependencyNode, create_dependency_graph,
5};
6use crate::find_buildpack_dirs;
7use crate::package_descriptor::buildpack_id_from_libcnb_dependency;
8use libcnb_common::toml_file::{TomlFileError, read_toml_file};
9use libcnb_data::buildpack::{BuildpackDescriptor, BuildpackId, BuildpackIdError};
10use libcnb_data::package_descriptor::PackageDescriptor;
11use petgraph::Graph;
12use std::convert::Infallible;
13use std::path::{Path, PathBuf};
14
15pub fn build_libcnb_buildpacks_dependency_graph(
29 cargo_workspace_root: &Path,
30) -> Result<Graph<BuildpackDependencyGraphNode, ()>, BuildBuildpackDependencyGraphError> {
31 find_buildpack_dirs(cargo_workspace_root)
32 .map_err(BuildBuildpackDependencyGraphError::FindBuildpackDirectories)
33 .and_then(|buildpack_directories| {
34 buildpack_directories
35 .iter()
36 .filter(|buildpack_directory| {
37 matches!(
38 determine_buildpack_kind(buildpack_directory),
39 Some(BuildpackKind::LibCnbRs | BuildpackKind::Composite)
40 )
41 })
42 .map(|buildpack_directory| {
43 build_libcnb_buildpack_dependency_graph_node(buildpack_directory)
44 })
45 .collect::<Result<Vec<_>, _>>()
46 })
47 .and_then(|nodes| {
48 create_dependency_graph(nodes)
49 .map_err(BuildBuildpackDependencyGraphError::CreateDependencyGraphError)
50 })
51}
52
53fn build_libcnb_buildpack_dependency_graph_node(
54 buildpack_directory: &Path,
55) -> Result<BuildpackDependencyGraphNode, BuildBuildpackDependencyGraphError> {
56 let buildpack_id =
57 read_toml_file::<BuildpackDescriptor>(buildpack_directory.join("buildpack.toml"))
58 .map_err(BuildBuildpackDependencyGraphError::ReadBuildpackDescriptorError)
59 .map(|buildpack_descriptor| buildpack_descriptor.buildpack().id.clone())?;
60
61 let package_toml_path = buildpack_directory.join("package.toml");
62 let dependencies = if package_toml_path.is_file() {
63 read_toml_file::<PackageDescriptor>(package_toml_path)
64 .map_err(BuildBuildpackDependencyGraphError::ReadPackageDescriptorError)
65 .and_then(|package_descriptor| {
66 get_buildpack_dependencies(&package_descriptor)
67 .map_err(BuildBuildpackDependencyGraphError::InvalidDependencyBuildpackId)
68 })?
69 } else {
70 Vec::new()
71 };
72
73 Ok(BuildpackDependencyGraphNode {
74 buildpack_id,
75 path: PathBuf::from(buildpack_directory),
76 dependencies,
77 })
78}
79
80#[derive(thiserror::Error, Debug)]
81pub enum BuildBuildpackDependencyGraphError {
82 #[error("Error while finding buildpack directories: {0}")]
83 FindBuildpackDirectories(ignore::Error),
84 #[error("Couldn't read buildpack.toml: {0}")]
85 ReadBuildpackDescriptorError(TomlFileError),
86 #[error("Couldn't read package.toml: {0}")]
87 ReadPackageDescriptorError(TomlFileError),
88 #[error("Dependency uses an invalid buildpack id: {0}")]
89 InvalidDependencyBuildpackId(BuildpackIdError),
90 #[error("Error while creating dependency graph: {0}")]
91 CreateDependencyGraphError(CreateDependencyGraphError<BuildpackId, Infallible>),
92}
93
94#[derive(Debug)]
95pub struct BuildpackDependencyGraphNode {
96 pub buildpack_id: BuildpackId,
97 pub path: PathBuf,
98 pub dependencies: Vec<BuildpackId>,
99}
100
101impl DependencyNode<BuildpackId, Infallible> for BuildpackDependencyGraphNode {
102 fn id(&self) -> BuildpackId {
103 self.buildpack_id.clone()
104 }
105
106 fn dependencies(&self) -> Result<Vec<BuildpackId>, Infallible> {
107 Ok(self.dependencies.clone())
108 }
109}
110
111fn get_buildpack_dependencies(
112 package_descriptor: &PackageDescriptor,
113) -> Result<Vec<BuildpackId>, BuildpackIdError> {
114 package_descriptor
115 .dependencies
116 .iter()
117 .filter_map(|dependency| buildpack_id_from_libcnb_dependency(dependency).transpose())
118 .collect()
119}