mod annotations;
mod artifact;
mod config;
mod descriptor;
mod digest;
mod index;
mod manifest;
mod oci_layout;
mod version;
use std::fmt::Display;
use serde::{Deserialize, Serialize};
pub use annotations::*;
pub use artifact::*;
pub use config::*;
pub use descriptor::*;
pub use digest::*;
pub use index::*;
pub use manifest::*;
pub use oci_layout::*;
pub use version::*;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MediaType {
Descriptor,
LayoutHeader,
ImageManifest,
ImageIndex,
ImageLayer,
ImageLayerGzip,
ImageLayerZstd,
ImageLayerNonDistributable,
ImageLayerNonDistributableGzip,
ImageLayerNonDistributableZstd,
ImageConfig,
ArtifactManifest,
EmptyJSON,
Other(String),
}
impl Display for MediaType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl From<&str> for MediaType {
fn from(media_type: &str) -> Self {
match media_type {
"application/vnd.oci.descriptor" => MediaType::Descriptor,
"application/vnd.oci.layout.header.v1+json" => MediaType::LayoutHeader,
"application/vnd.oci.image.manifest.v1+json" => MediaType::ImageManifest,
"application/vnd.oci.image.index.v1+json" => MediaType::ImageIndex,
"application/vnd.oci.image.layer.v1.tar" => MediaType::ImageLayer,
"application/vnd.oci.image.layer.v1.tar+gzip" => MediaType::ImageLayerGzip,
"application/vnd.oci.image.layer.v1.tar+zstd" => MediaType::ImageLayerZstd,
"application/vnd.oci.image.layer.nondistributable.v1.tar" => {
MediaType::ImageLayerNonDistributable
}
"application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" => {
MediaType::ImageLayerNonDistributableGzip
}
"application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" => {
MediaType::ImageLayerNonDistributableZstd
}
"application/vnd.oci.image.config.v1+json" => MediaType::ImageConfig,
"application/vnd.oci.artifact.manifest.v1+json" => MediaType::ArtifactManifest,
"application/vnd.oci.empty.v1+json" => MediaType::EmptyJSON,
media => MediaType::Other(media.to_owned()),
}
}
}
impl From<MediaType> for String {
fn from(media_type: MediaType) -> Self {
media_type.as_ref().to_owned()
}
}
impl AsRef<str> for MediaType {
fn as_ref(&self) -> &str {
match self {
Self::Descriptor => "application/vnd.oci.descriptor",
Self::LayoutHeader => "application/vnd.oci.layout.header.v1+json",
Self::ImageManifest => "application/vnd.oci.image.manifest.v1+json",
Self::ImageIndex => "application/vnd.oci.image.index.v1+json",
Self::ImageLayer => "application/vnd.oci.image.layer.v1.tar",
Self::ImageLayerGzip => "application/vnd.oci.image.layer.v1.tar+gzip",
Self::ImageLayerZstd => "application/vnd.oci.image.layer.v1.tar+zstd",
Self::ImageLayerNonDistributable => {
"application/vnd.oci.image.layer.nondistributable.v1.tar"
}
Self::ImageLayerNonDistributableGzip => {
"application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
}
Self::ImageLayerNonDistributableZstd => {
"application/vnd.oci.image.layer.nondistributable.v1.tar+zstd"
}
Self::ImageConfig => "application/vnd.oci.image.config.v1+json",
Self::ArtifactManifest => "application/vnd.oci.artifact.manifest.v1+json",
Self::EmptyJSON => "application/vnd.oci.empty.v1+json",
Self::Other(media_type) => media_type.as_str(),
}
}
}
pub trait ToDockerV2S2 {
fn to_docker_v2s2(&self) -> Result<&str, std::fmt::Error>;
}
impl ToDockerV2S2 for MediaType {
fn to_docker_v2s2(&self) -> Result<&str, std::fmt::Error> {
Ok(match self {
Self::ImageIndex => "application/vnd.docker.distribution.manifest.list.v2+json",
Self::ImageManifest => "application/vnd.docker.distribution.manifest.v2+json",
Self::ImageConfig => "application/vnd.docker.container.image.v1+json",
Self::ImageLayerGzip => "application/vnd.docker.image.rootfs.diff.tar.gzip",
_ => return Err(std::fmt::Error),
})
}
}
impl Serialize for MediaType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let media_type = format!("{self}");
media_type.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for MediaType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let media_type = String::deserialize(deserializer)?;
Ok(media_type.as_str().into())
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Os {
AIX,
Android,
Darwin,
DragonFlyBSD,
FreeBSD,
Hurd,
Illumos,
#[allow(non_camel_case_types)]
iOS,
Js,
Linux,
Nacl,
NetBSD,
OpenBSD,
Plan9,
Solaris,
Windows,
#[allow(non_camel_case_types)]
zOS,
Other(String),
}
impl From<&str> for Os {
fn from(os: &str) -> Self {
match os {
"aix" => Os::AIX,
"android" => Os::Android,
"darwin" => Os::Darwin,
"dragonfly" => Os::DragonFlyBSD,
"freebsd" => Os::FreeBSD,
"hurd" => Os::Hurd,
"illumos" => Os::Illumos,
"ios" => Os::iOS,
"js" => Os::Js,
"linux" => Os::Linux,
"nacl" => Os::Nacl,
"netbsd" => Os::NetBSD,
"openbsd" => Os::OpenBSD,
"plan9" => Os::Plan9,
"solaris" => Os::Solaris,
"windows" => Os::Windows,
"zos" => Os::zOS,
name => Os::Other(name.to_owned()),
}
}
}
impl Display for Os {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let print = match self {
Os::AIX => "aix",
Os::Android => "android",
Os::Darwin => "darwin",
Os::DragonFlyBSD => "dragonfly",
Os::FreeBSD => "freebsd",
Os::Hurd => "hurd",
Os::Illumos => "illumos",
Os::iOS => "ios",
Os::Js => "js",
Os::Linux => "linux",
Os::Nacl => "nacl",
Os::NetBSD => "netbsd",
Os::OpenBSD => "openbsd",
Os::Plan9 => "plan9",
Os::Solaris => "solaris",
Os::Windows => "windows",
Os::zOS => "zos",
Os::Other(name) => name,
};
write!(f, "{print}")
}
}
impl Serialize for Os {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let os = format!("{self}");
os.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Os {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let os = String::deserialize(deserializer)?;
Ok(os.as_str().into())
}
}
impl Default for Os {
fn default() -> Self {
Os::from(std::env::consts::OS)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Arch {
#[allow(non_camel_case_types)]
i386,
Amd64,
Amd64p32,
ARM,
ARMbe,
ARM64,
ARM64be,
LoongArch64,
Mips,
Mipsle,
Mips64,
Mips64le,
Mips64p32,
Mips64p32le,
PowerPC,
PowerPC64,
PowerPC64le,
RISCV,
RISCV64,
#[allow(non_camel_case_types)]
s390,
#[allow(non_camel_case_types)]
s390x,
SPARC,
SPARC64,
Wasm,
Other(String),
}
impl Display for Arch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let print = match self {
Arch::i386 => "386",
Arch::Amd64 => "amd64",
Arch::Amd64p32 => "amd64p32",
Arch::ARM => "arm",
Arch::ARMbe => "armbe",
Arch::ARM64 => "arm64",
Arch::ARM64be => "arm64be",
Arch::LoongArch64 => "loong64",
Arch::Mips => "mips",
Arch::Mipsle => "mipsle",
Arch::Mips64 => "mips64",
Arch::Mips64le => "mips64le",
Arch::Mips64p32 => "mips64p32",
Arch::Mips64p32le => "mips64p32le",
Arch::PowerPC => "ppc",
Arch::PowerPC64 => "ppc64",
Arch::PowerPC64le => "ppc64le",
Arch::RISCV => "riscv",
Arch::RISCV64 => "riscv64",
Arch::s390 => "s390",
Arch::s390x => "s390x",
Arch::SPARC => "sparc",
Arch::SPARC64 => "sparc64",
Arch::Wasm => "wasm",
Arch::Other(arch) => arch,
};
write!(f, "{print}")
}
}
impl From<&str> for Arch {
fn from(arch: &str) -> Self {
match arch {
"386" => Arch::i386,
"amd64" => Arch::Amd64,
"amd64p32" => Arch::Amd64p32,
"arm" => Arch::ARM,
"armbe" => Arch::ARM64be,
"arm64" => Arch::ARM64,
"arm64be" => Arch::ARM64be,
"loong64" => Arch::LoongArch64,
"mips" => Arch::Mips,
"mipsle" => Arch::Mipsle,
"mips64" => Arch::Mips64,
"mips64le" => Arch::Mips64le,
"mips64p32" => Arch::Mips64p32,
"mips64p32le" => Arch::Mips64p32le,
"ppc" => Arch::PowerPC,
"ppc64" => Arch::PowerPC64,
"ppc64le" => Arch::PowerPC64le,
"riscv" => Arch::RISCV,
"riscv64" => Arch::RISCV64,
"s390" => Arch::s390,
"s390x" => Arch::s390x,
"sparc" => Arch::SPARC,
"sparc64" => Arch::SPARC64,
"wasm" => Arch::Wasm,
arch => Arch::Other(arch.to_owned()),
}
}
}
impl Serialize for Arch {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let arch = format!("{self}");
arch.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Arch {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let arch = String::deserialize(deserializer)?;
Ok(arch.as_str().into())
}
}
impl Default for Arch {
fn default() -> Self {
let goarch = match std::env::consts::ARCH {
"x86_64" => "amd64",
"aarch64" => "arm64",
"powerpc64" if cfg!(target_endian = "big") => "ppc64",
"powerpc64" if cfg!(target_endian = "little") => "ppc64le",
o => o,
};
Arch::from(goarch)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arch_translation() {
let a = Arch::default();
if let Arch::Other(o) = a {
panic!("Architecture {o} not mapped between Rust and OCI")
}
}
#[test]
fn test_asref() {
assert_eq!(
MediaType::ImageConfig.as_ref(),
"application/vnd.oci.image.config.v1+json"
);
assert_eq!(
String::from(MediaType::ImageConfig).as_str(),
"application/vnd.oci.image.config.v1+json"
);
}
}