libcnb_data/
package_descriptor.rsuse crate::package_descriptor::PlatformOs::Linux;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::path::PathBuf;
use uriparse::{URIReference, URIReferenceError};
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct PackageDescriptor {
pub buildpack: PackageDescriptorBuildpackReference,
#[serde(default)]
pub dependencies: Vec<PackageDescriptorDependency>,
#[serde(default)]
pub platform: Platform,
}
impl Default for PackageDescriptor {
fn default() -> Self {
PackageDescriptor {
buildpack: PackageDescriptorBuildpackReference::try_from(".")
.expect("a package.toml with buildpack.uri=\".\" should be valid"),
dependencies: Vec::new(),
platform: Platform::default(),
}
}
}
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(deny_unknown_fields)]
pub struct PackageDescriptorBuildpackReference {
#[serde(deserialize_with = "deserialize_uri_reference")]
#[serde(serialize_with = "serialize_uri_reference")]
pub uri: URIReference<'static>,
}
#[derive(Debug)]
pub enum PackageDescriptorBuildpackError {
InvalidUri(String),
}
impl TryFrom<&str> for PackageDescriptorBuildpackReference {
type Error = PackageDescriptorBuildpackError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
try_uri_from_str(value)
.map(|uri| PackageDescriptorBuildpackReference { uri })
.map_err(|_| PackageDescriptorBuildpackError::InvalidUri(value.to_string()))
}
}
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(deny_unknown_fields)]
pub struct PackageDescriptorDependency {
#[serde(deserialize_with = "deserialize_uri_reference")]
#[serde(serialize_with = "serialize_uri_reference")]
pub uri: URIReference<'static>,
}
#[derive(thiserror::Error, Debug)]
pub enum PackageDescriptorDependencyError {
#[error("Invalid URI: {0}")]
InvalidUri(String),
}
impl TryFrom<PathBuf> for PackageDescriptorDependency {
type Error = PackageDescriptorDependencyError;
fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
Self::try_from(value.to_string_lossy().to_string().as_str())
}
}
impl TryFrom<&str> for PackageDescriptorDependency {
type Error = PackageDescriptorDependencyError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
try_uri_from_str(value)
.map(|uri| PackageDescriptorDependency { uri })
.map_err(|_| PackageDescriptorDependencyError::InvalidUri(value.to_string()))
}
}
fn try_uri_from_str(value: &str) -> Result<URIReference<'static>, URIReferenceError> {
URIReference::try_from(value).map(URIReference::into_owned)
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Platform {
pub os: PlatformOs,
}
impl Default for Platform {
fn default() -> Self {
Self { os: Linux }
}
}
#[derive(Debug, Deserialize, Serialize, Eq, PartialEq, Clone)]
#[serde(rename_all = "lowercase")]
pub enum PlatformOs {
Linux,
Windows,
}
fn deserialize_uri_reference<'de, D>(deserializer: D) -> Result<URIReference<'static>, D::Error>
where
D: Deserializer<'de>,
{
let value = String::deserialize(deserializer)?;
let uri = URIReference::try_from(value.as_str()).map_err(serde::de::Error::custom)?;
Ok(uri.into_owned())
}
fn serialize_uri_reference<S>(uri: &URIReference, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let value = uri.to_string();
serializer.serialize_str(value.as_str())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::package_descriptor::PlatformOs::Windows;
#[test]
fn it_parses_minimal() {
let toml_str = r#"
[buildpack]
uri = "."
"#;
let package_descriptor = toml::from_str::<PackageDescriptor>(toml_str).unwrap();
assert_eq!(
package_descriptor.buildpack,
PackageDescriptorBuildpackReference::try_from(".").unwrap()
);
assert_eq!(package_descriptor.platform.os, Linux);
}
#[test]
fn it_parses_with_dependencies_and_platform() {
let toml_str = r#"
[buildpack]
uri = "."
[[dependencies]]
uri = "libcnb:buildpack-id"
[[dependencies]]
uri = "../relative/path"
[[dependencies]]
uri = "/absolute/path"
[[dependencies]]
uri = "docker://docker.io/heroku/example:1.2.3"
[platform]
os = "windows"
"#;
let package_descriptor = toml::from_str::<PackageDescriptor>(toml_str).unwrap();
assert_eq!(
package_descriptor.buildpack,
PackageDescriptorBuildpackReference::try_from(".").unwrap()
);
assert_eq!(package_descriptor.platform.os, Windows);
assert_eq!(
package_descriptor.dependencies,
[
PackageDescriptorDependency::try_from("libcnb:buildpack-id").unwrap(),
PackageDescriptorDependency::try_from("../relative/path").unwrap(),
PackageDescriptorDependency::try_from("/absolute/path").unwrap(),
PackageDescriptorDependency::try_from("docker://docker.io/heroku/example:1.2.3")
.unwrap()
]
);
}
#[test]
fn it_serializes() {
let package_descriptor = PackageDescriptor {
buildpack: PackageDescriptorBuildpackReference::try_from(".").unwrap(),
dependencies: vec![
PackageDescriptorDependency::try_from("libcnb:buildpack-id").unwrap(),
PackageDescriptorDependency::try_from("../relative/path").unwrap(),
PackageDescriptorDependency::try_from("/absolute/path").unwrap(),
PackageDescriptorDependency::try_from("docker://docker.io/heroku/example:1.2.3")
.unwrap(),
],
platform: Platform::default(),
};
let package_descriptor_contents = toml::to_string(&package_descriptor).unwrap();
assert_eq!(
package_descriptor_contents,
r#"
[buildpack]
uri = "."
[[dependencies]]
uri = "libcnb:buildpack-id"
[[dependencies]]
uri = "../relative/path"
[[dependencies]]
uri = "/absolute/path"
[[dependencies]]
uri = "docker://docker.io/heroku/example:1.2.3"
[platform]
os = "linux"
"#
.trim_start()
);
}
}