diff --git a/CHANGELOG.md b/CHANGELOG.md index c750c804..79e2bbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `libherokubuildpack`: - - Removed `error` and `log` modules in favor of `buildpack_output`. ([#721](https://github.com/heroku/libcnb.rs/pull/721)) + - Removed the `log` module in favor of `buildpack_output`. ([#721](https://github.com/heroku/libcnb.rs/pull/721)) ### Added diff --git a/libherokubuildpack/Cargo.toml b/libherokubuildpack/Cargo.toml index c5d7ef78..4ed4c4d2 100644 --- a/libherokubuildpack/Cargo.toml +++ b/libherokubuildpack/Cargo.toml @@ -21,6 +21,7 @@ workspace = true default = ["command", "download", "digest", "tar", "toml", "fs", "write", "buildpack_output"] download = ["dep:ureq", "dep:thiserror"] digest = ["dep:sha2"] +error = ["buildpack_output", "dep:libcnb"] tar = ["dep:tar", "dep:flate2"] toml = ["dep:toml"] fs = ["dep:pathdiff"] @@ -40,7 +41,6 @@ libcnb = { workspace = true, optional = true } pathdiff = { version = "0.2.1", optional = true } sha2 = { version = "0.10.8", optional = true } tar = { version = "0.4.40", default-features = false, optional = true } -termcolor = { version = "1.4.1", optional = true } thiserror = { version = "1.0.57", optional = true } toml = { workspace = true, optional = true } ureq = { version = "2.9.5", default-features = false, features = ["tls"], optional = true } diff --git a/libherokubuildpack/src/error.rs b/libherokubuildpack/src/error.rs new file mode 100644 index 00000000..e6c4be5f --- /dev/null +++ b/libherokubuildpack/src/error.rs @@ -0,0 +1,71 @@ +use crate::buildpack_output::BuildpackOutput; +use std::fmt::Debug; + +/// Handles a given [`libcnb::Error`] in a consistent style. +/// +/// This function is intended to be used inside [`libcnb::Buildpack::on_error`]. +/// +/// It outputs generic libcnb errors in a consistent style using the [logging functions](log_error) from this +/// crate. Buildpack specific errors are handled by the passed custom handler. +/// +/// # Example: +/// ``` +/// use libcnb::build::{BuildContext, BuildResult}; +/// use libcnb::Buildpack; +/// use libcnb::detect::{DetectContext, DetectResult}; +/// use libcnb::generic::{GenericMetadata, GenericPlatform}; +/// use libherokubuildpack::log::log_error; +/// use libherokubuildpack::error::on_error; +/// +/// #[derive(Debug)] +/// enum FooBuildpackError { +/// CannotExecuteFooBuildTool(std::io::Error), +/// InvalidFooDescriptorToml +/// } +/// +/// fn on_foo_buildpack_error(e: FooBuildpackError) { +/// match e { +/// FooBuildpackError::InvalidFooDescriptorToml => { +/// log_error("Invalid foo.toml", "Your app's foo.toml is invalid!"); +/// } +/// FooBuildpackError::CannotExecuteFooBuildTool(inner) => { +/// log_error("Couldn't execute foo build tool", format!("Cause: {}", &inner)); +/// } +/// } +/// } +/// +/// struct FooBuildpack; +/// +/// impl Buildpack for FooBuildpack { +/// type Platform = GenericPlatform; +/// type Metadata = GenericMetadata; +/// type Error = FooBuildpackError; +/// +/// // Omitted detect and build implementations... +/// # fn detect(&self, context: DetectContext) -> libcnb::Result { +/// # unimplemented!() +/// # } +/// # +/// # fn build(&self, context: BuildContext) -> libcnb::Result { +/// # unimplemented!() +/// # } +/// +/// fn on_error(&self, error: libcnb::Error) { +/// on_error(on_foo_buildpack_error, error) +/// } +/// } +/// ``` +pub fn on_error(f: F, error: libcnb::Error) +where + E: Debug, + F: Fn(E), +{ + match error { + libcnb::Error::BuildpackError(buildpack_error) => f(buildpack_error), + libcnb_error => { + BuildpackOutput::new(std::io::stdout()) + .start_silent() + .error(format!("Internal Buildpack Error\n\n{libcnb_error}")); + } + } +} diff --git a/libherokubuildpack/src/lib.rs b/libherokubuildpack/src/lib.rs index 27ffb187..c4b23dcd 100644 --- a/libherokubuildpack/src/lib.rs +++ b/libherokubuildpack/src/lib.rs @@ -8,6 +8,8 @@ pub mod command; pub mod digest; #[cfg(feature = "download")] pub mod download; +#[cfg(feature = "error")] +pub mod error; #[cfg(feature = "fs")] pub mod fs; #[cfg(feature = "tar")]