libcnb/layer/struct_api/
mod.rs

1pub(crate) mod handling;
2
3// BuildContext is only used in RustDoc (https://github.com/rust-lang/rust/issues/79542)
4use crate::Buildpack;
5#[allow(unused)]
6use crate::build::BuildContext;
7use crate::layer::shared::{WriteLayerError, replace_layer_exec_d_programs, replace_layer_sboms};
8use crate::layer::{LayerError, ReadLayerError};
9use crate::layer_env::LayerEnv;
10use crate::sbom::Sbom;
11use libcnb_data::generic::GenericMetadata;
12use libcnb_data::layer::LayerName;
13use serde::Serialize;
14use std::borrow::Borrow;
15use std::collections::HashMap;
16use std::marker::PhantomData;
17use std::path::{Path, PathBuf};
18
19/// A definition for a cached layer.
20///
21/// Refer to the docs of [`BuildContext::cached_layer`] for usage examples.
22pub struct CachedLayerDefinition<'a, M, MA, RA> {
23    /// Whether the layer is intended for build.
24    pub build: bool,
25    /// Whether the layer is intended for launch.
26    pub launch: bool,
27    /// Callback for when the metadata of a restored layer cannot be parsed as `M`.
28    ///
29    /// Allows replacing the metadata before continuing (i.e. migration to a newer version) or
30    /// deleting the layer.
31    pub invalid_metadata_action: &'a dyn Fn(&GenericMetadata) -> MA,
32    /// Callback when the layer was restored from cache to validate the contents and metadata.
33    /// Can be used to delete existing cached layers.
34    pub restored_layer_action: &'a dyn Fn(&M, &Path) -> RA,
35}
36
37/// A definition for an uncached layer.
38///
39/// Refer to the docs of [`BuildContext::uncached_layer`] for usage examples.
40pub struct UncachedLayerDefinition {
41    /// Whether the layer is intended for build.
42    pub build: bool,
43    /// Whether the layer is intended for launch.
44    pub launch: bool,
45}
46
47/// The action to take when the layer metadata is invalid.
48#[derive(Copy, Clone, Debug)]
49pub enum InvalidMetadataAction<M> {
50    /// Delete the existing layer.
51    DeleteLayer,
52    /// Keep the layer, but replace the metadata. Commonly used to migrate to a newer
53    /// metadata format.
54    ReplaceMetadata(M),
55}
56
57/// The action to take when a previously cached layer was restored.
58#[derive(Copy, Clone, Debug)]
59pub enum RestoredLayerAction {
60    /// Delete the restored layer.
61    DeleteLayer,
62    /// Keep the restored layer. It can then be used as-is or updated if required.
63    KeepLayer,
64}
65
66/// Framework metadata about the layer state.
67///
68/// See: [`BuildContext::cached_layer`] and [`BuildContext::uncached_layer`]
69#[derive(Copy, Clone, Debug, Eq, PartialEq)]
70pub enum LayerState<MAC, RAC> {
71    /// The layer contains validated cached contents from a previous buildpack run.
72    ///
73    /// See: `restored_layer_action` in [`CachedLayerDefinition`].
74    Restored { cause: RAC },
75    /// The layer is empty. Inspect the contained [`EmptyLayerCause`] for the cause.
76    Empty { cause: EmptyLayerCause<MAC, RAC> },
77}
78
79/// The cause of a layer being empty.
80#[derive(Copy, Clone, Debug, Eq, PartialEq)]
81pub enum EmptyLayerCause<MAC, RAC> {
82    /// The layer wasn't cached in a previous buildpack run and was newly created.
83    NewlyCreated,
84    /// The layer was cached in a previous buildpack run, but the metadata was invalid and couldn't
85    /// be converted into a valid form. Subsequently, the layer was deleted entirely.
86    ///
87    /// See: `invalid_metadata_action` in [`CachedLayerDefinition`].
88    InvalidMetadataAction { cause: MAC },
89    /// The layer was cached in a previous buildpack run, but the `restored_layer_action` function
90    /// rejected the contents and/or metadata.
91    ///
92    /// See: `restored_layer_action` in [`CachedLayerDefinition`].
93    RestoredLayerAction { cause: RAC },
94}
95
96/// A value-to-value conversion for layer actions.
97///
98/// Similar to [`Into`], but specialized. Allowing it to also be implemented for
99/// values in the standard library such as [`Result`].
100///
101/// Implement this trait if you want to use your own types as actions.
102///
103/// libcnb ships with generic implementations for the majority of the use-cases:
104/// - Using [`RestoredLayerAction`] or [`InvalidMetadataAction`] directly.
105/// - Using [`RestoredLayerAction`] or [`InvalidMetadataAction`] directly, wrapped in a Result.
106/// - Using [`RestoredLayerAction`] or [`InvalidMetadataAction`] with a cause value in a tuple.
107/// - Using [`RestoredLayerAction`] or [`InvalidMetadataAction`] with a cause value in a tuple, wrapped in a Result.
108pub trait IntoAction<T, C, E> {
109    fn into_action(self) -> Result<(T, C), E>;
110}
111
112// Allows to use the layer actions directly.
113impl<T, E> IntoAction<T, (), E> for T {
114    fn into_action(self) -> Result<(T, ()), E> {
115        Ok((self, ()))
116    }
117}
118
119//  Allows to use the layer actions directly wrapped in a Result.
120impl<T, E> IntoAction<T, (), E> for Result<T, E> {
121    fn into_action(self) -> Result<(T, ()), E> {
122        self.map(|value| (value, ()))
123    }
124}
125
126// Allows to use the layer actions directly with a cause as a tuple.
127impl<T, C, E> IntoAction<T, C, E> for (T, C) {
128    fn into_action(self) -> Result<(T, C), E> {
129        Ok(self)
130    }
131}
132
133// Allows to use the layer actions directly with a cause as a tuple wrapped in a Result.
134impl<T, C, E> IntoAction<T, C, E> for Result<(T, C), E> {
135    fn into_action(self) -> Result<(T, C), E> {
136        self
137    }
138}
139
140/// A reference to an existing layer on disk.
141///
142/// Provides functions to modify the layer such as replacing its metadata, environment, SBOMs or
143/// exec.d programs.
144///
145/// To obtain a such a reference, use [`BuildContext::cached_layer`] or [`BuildContext::uncached_layer`].
146pub struct LayerRef<B, MAC, RAC>
147where
148    B: Buildpack + ?Sized,
149{
150    name: LayerName,
151    // Technically not part of the layer itself. However, the functions that modify the layer
152    // will need a reference to the layers directory as they will also modify files outside the
153    // actual layer directory. To make LayerRef nice to use, we bite the bullet and include
154    // the layers_dir here.
155    layers_dir: PathBuf,
156    buildpack: PhantomData<B>,
157    pub state: LayerState<MAC, RAC>,
158}
159
160impl<B, MAC, RAC> LayerRef<B, MAC, RAC>
161where
162    B: Buildpack,
163{
164    /// Returns the path to the layer on disk.
165    pub fn path(&self) -> PathBuf {
166        self.layers_dir.join(self.name.as_str())
167    }
168
169    /// Writes the given layer metadata to disk.
170    ///
171    /// Any existing layer metadata will be overwritten. The new value does not have to be of the
172    /// same type as the existing metadata.
173    pub fn write_metadata<M>(&self, metadata: M) -> crate::Result<(), B::Error>
174    where
175        M: Serialize,
176    {
177        crate::layer::shared::replace_layer_metadata(&self.layers_dir, &self.name, metadata)
178            .map_err(|error| {
179                crate::Error::LayerError(LayerError::WriteLayerError(
180                    WriteLayerError::WriteLayerMetadataError(error),
181                ))
182            })
183    }
184
185    /// Writes the given layer environment to disk.
186    ///
187    /// Any existing layer environment will be overwritten.
188    pub fn write_env(&self, env: impl Borrow<LayerEnv>) -> crate::Result<(), B::Error> {
189        env.borrow()
190            .write_to_layer_dir(self.path())
191            .map_err(|error| {
192                crate::Error::LayerError(LayerError::WriteLayerError(WriteLayerError::IoError(
193                    error,
194                )))
195            })
196    }
197
198    /// Reads the current layer environment from disk.
199    ///
200    /// Note that this includes implicit entries such as adding `bin/` to `PATH`. See [`LayerEnv`]
201    /// docs for details on implicit entries.
202    pub fn read_env(&self) -> crate::Result<LayerEnv, B::Error> {
203        LayerEnv::read_from_layer_dir(self.path()).map_err(|error| {
204            crate::Error::LayerError(LayerError::ReadLayerError(ReadLayerError::IoError(error)))
205        })
206    }
207
208    /// Writes the given SBOMs to disk.
209    ///
210    /// Any existing SBOMs will be overwritten.
211    pub fn write_sboms(&self, sboms: &[Sbom]) -> crate::Result<(), B::Error> {
212        replace_layer_sboms(&self.layers_dir, &self.name, sboms).map_err(|error| {
213            crate::Error::LayerError(LayerError::WriteLayerError(
214                WriteLayerError::ReplaceLayerSbomsError(error),
215            ))
216        })
217    }
218
219    /// Writes the given exec.d programs to disk.
220    ///
221    /// Any existing exec.d programs will be overwritten.
222    pub fn write_exec_d_programs<P, S>(&self, programs: P) -> crate::Result<(), B::Error>
223    where
224        S: Into<String>,
225        P: IntoIterator<Item = (S, PathBuf)>,
226    {
227        let programs = programs
228            .into_iter()
229            .map(|(k, v)| (k.into(), v))
230            .collect::<HashMap<_, _>>();
231
232        replace_layer_exec_d_programs(&self.layers_dir, &self.name, &programs).map_err(|error| {
233            crate::Error::LayerError(LayerError::WriteLayerError(
234                WriteLayerError::ReplaceLayerExecdProgramsError(error),
235            ))
236        })
237    }
238}