#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::fmt;
use std::error::Error;
use use_oci_digest::OciDigest;
use use_oci_media_type::{KnownMediaType, OciMediaType};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LayerError {
InvalidMediaType,
}
impl fmt::Display for LayerError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidMediaType => {
formatter.write_str("media type is not an OCI layer media type")
},
}
}
}
impl Error for LayerError {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum LayerCompression {
Uncompressed,
Gzip,
Zstd,
}
impl LayerCompression {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Uncompressed => "uncompressed",
Self::Gzip => "gzip",
Self::Zstd => "zstd",
}
}
}
impl fmt::Display for LayerCompression {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum LayerKind {
Filesystem,
Foreign,
Nondistributable,
Unknown,
}
impl Default for LayerKind {
fn default() -> Self {
Self::Filesystem
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct LayerSize(u64);
impl LayerSize {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct LayerMediaType(OciMediaType);
impl LayerMediaType {
pub fn new(media_type: OciMediaType) -> Result<Self, LayerError> {
if media_type.is_layer() {
Ok(Self(media_type))
} else {
Err(LayerError::InvalidMediaType)
}
}
#[must_use]
pub const fn tar() -> Self {
Self(OciMediaType::Known(KnownMediaType::LayerTar))
}
#[must_use]
pub const fn gzip_tar() -> Self {
Self(OciMediaType::Known(KnownMediaType::LayerTarGzip))
}
#[must_use]
pub const fn zstd_tar() -> Self {
Self(OciMediaType::Known(KnownMediaType::LayerTarZstd))
}
#[must_use]
pub const fn media_type(&self) -> &OciMediaType {
&self.0
}
#[must_use]
pub const fn compression(&self) -> LayerCompression {
match self.0 {
OciMediaType::Known(KnownMediaType::LayerTarGzip) => LayerCompression::Gzip,
OciMediaType::Known(KnownMediaType::LayerTarZstd) => LayerCompression::Zstd,
_ => LayerCompression::Uncompressed,
}
}
}
impl fmt::Display for LayerMediaType {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(formatter)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DiffId(OciDigest);
impl DiffId {
#[must_use]
pub const fn new(digest: OciDigest) -> Self {
Self(digest)
}
#[must_use]
pub const fn digest(&self) -> &OciDigest {
&self.0
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct OciLayer {
media_type: LayerMediaType,
digest: OciDigest,
size: LayerSize,
diff_id: Option<DiffId>,
kind: LayerKind,
}
impl OciLayer {
#[must_use]
pub fn new(media_type: LayerMediaType, digest: OciDigest, size: LayerSize) -> Self {
Self {
media_type,
digest,
size,
diff_id: None,
kind: LayerKind::Filesystem,
}
}
#[must_use]
pub fn with_diff_id(mut self, diff_id: DiffId) -> Self {
self.diff_id = Some(diff_id);
self
}
#[must_use]
pub const fn with_kind(mut self, kind: LayerKind) -> Self {
self.kind = kind;
self
}
#[must_use]
pub const fn media_type(&self) -> &LayerMediaType {
&self.media_type
}
#[must_use]
pub const fn digest(&self) -> &OciDigest {
&self.digest
}
#[must_use]
pub const fn size(&self) -> LayerSize {
self.size
}
#[must_use]
pub const fn compression(&self) -> LayerCompression {
self.media_type.compression()
}
}
#[cfg(test)]
mod tests {
use super::{LayerCompression, LayerMediaType, LayerSize, OciLayer};
use use_oci_digest::OciDigest;
use use_oci_media_type::OciMediaType;
const SHA: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
#[test]
fn models_layer_metadata_without_extraction() -> Result<(), Box<dyn std::error::Error>> {
let digest: OciDigest = format!("sha256:{SHA}").parse()?;
let layer = OciLayer::new(LayerMediaType::gzip_tar(), digest, LayerSize::new(42));
assert_eq!(layer.compression(), LayerCompression::Gzip);
assert_eq!(layer.size().as_u64(), 42);
assert!(LayerMediaType::new(OciMediaType::image_config()).is_err());
Ok(())
}
}