Skip to content

Commit

Permalink
Aptfile parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
colincasey committed Feb 12, 2024
1 parent 07980b0 commit 98a8291
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 7 deletions.
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ rust-version = "1.75"

[dependencies]
libcnb = { version = "=0.17.0", features = ["trace"] }
serde = "1"

[dev-dependencies]
libcnb-test = "=0.17.0"
indoc = "2"

[profile.release]
strip = true
103 changes: 103 additions & 0 deletions src/aptfile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::ffi::OsStr;
use std::ops::Deref;
use std::str::FromStr;

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub(crate) struct Aptfile {
pub(crate) packages: HashSet<DebianPackage>,
}

#[derive(Debug)]
pub(crate) struct ParseAptfileError(ParseDebianPackageError);

impl FromStr for Aptfile {
type Err = ParseAptfileError;

fn from_str(value: &str) -> Result<Self, Self::Err> {
value
.lines()
.filter_map(|mut line| {
line = line.trim();
if line.starts_with('#') || line.is_empty() {
None
} else {
Some(line)
}
})
.map(|line| line.parse::<DebianPackage>())
.collect::<Result<HashSet<_>, _>>()
.map_err(ParseAptfileError)
.map(|packages| Aptfile { packages })
}
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub(crate) struct DebianPackage(String);

impl Deref for DebianPackage {
type Target = String;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl AsRef<OsStr> for DebianPackage {
fn as_ref(&self) -> &OsStr {
OsStr::new(&self.0)
}
}

#[derive(Debug, PartialEq)]
pub(crate) struct ParseDebianPackageError(String);

impl FromStr for DebianPackage {
type Err = ParseDebianPackageError;

fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.is_empty() {
Err(ParseDebianPackageError(value.to_string()))
} else {
Ok(DebianPackage(value.to_string()))
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;

#[test]
fn parse_valid_debian_package() {
let debian_package = DebianPackage::from_str("package-name").unwrap();
assert_eq!(*debian_package, "package-name".to_string());
}
#[test]
fn parse_invalid_debian_package() {
let error = DebianPackage::from_str("").unwrap_err();
assert_eq!(error, ParseDebianPackageError("".to_string()));
}

#[test]
fn parse_aptfile() {
let aptfile = Aptfile::from_str(indoc! { "
# comment line
package-name-1
package-name-2
" })
.unwrap();
assert_eq!(
aptfile.packages,
HashSet::from([
DebianPackage::from_str("package-name-1").unwrap(),
DebianPackage::from_str("package-name-2").unwrap(),
])
);
}
}
13 changes: 12 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
#[derive(Debug)]
pub(crate) enum AptBuildpackError {}
#[allow(clippy::enum_variant_names)]
pub(crate) enum AptBuildpackError {
DetectAptfile(std::io::Error),
ReadAptfile(std::io::Error),
ParseAptfile,
}

impl From<AptBuildpackError> for libcnb::Error<AptBuildpackError> {
fn from(value: AptBuildpackError) -> Self {
libcnb::Error::BuildpackError(value)
}
}
32 changes: 26 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
use crate::aptfile::Aptfile;
use crate::errors::AptBuildpackError;
use libcnb::build::{BuildContext, BuildResult};
use libcnb::detect::{DetectContext, DetectResult};
use libcnb::build::{BuildContext, BuildResult, BuildResultBuilder};
use libcnb::detect::{DetectContext, DetectResult, DetectResultBuilder};
use libcnb::generic::{GenericMetadata, GenericPlatform};
use libcnb::{buildpack_main, Buildpack};
use std::fs;

mod aptfile;
mod errors;

buildpack_main!(AptBuildpack);

const APTFILE_PATH: &str = "Aptfile";

struct AptBuildpack;

impl Buildpack for AptBuildpack {
type Platform = GenericPlatform;
type Metadata = GenericMetadata;
type Error = AptBuildpackError;

fn detect(&self, _context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
todo!()
fn detect(&self, context: DetectContext<Self>) -> libcnb::Result<DetectResult, Self::Error> {
let exists = context
.app_dir
.join(APTFILE_PATH)
.try_exists()
.map_err(AptBuildpackError::DetectAptfile)?;

if exists {
DetectResultBuilder::pass().build()
} else {
DetectResultBuilder::fail().build()
}
}

fn build(&self, _context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
todo!()
fn build(&self, context: BuildContext<Self>) -> libcnb::Result<BuildResult, Self::Error> {
let _aptfile: Aptfile = fs::read_to_string(context.app_dir.join(APTFILE_PATH))
.map_err(AptBuildpackError::ReadAptfile)?
.parse()
.map_err(|_| AptBuildpackError::ParseAptfile)?;

BuildResultBuilder::new().build()
}
}

0 comments on commit 98a8291

Please sign in to comment.