1mod annotations;
4mod artifact;
5mod config;
6mod descriptor;
7mod digest;
8mod index;
9mod manifest;
10mod oci_layout;
11mod version;
12
13use std::fmt::Display;
14
15use serde::{Deserialize, Serialize};
16
17pub use annotations::*;
18pub use artifact::*;
19pub use config::*;
20pub use descriptor::*;
21pub use digest::*;
22pub use index::*;
23pub use manifest::*;
24pub use oci_layout::*;
25pub use version::*;
26
27#[derive(Clone, Debug, PartialEq, Eq)]
30pub enum MediaType {
31    Descriptor,
33    LayoutHeader,
35    ImageManifest,
37    ImageIndex,
39    ImageLayer,
42    ImageLayerGzip,
45    ImageLayerZstd,
48    ImageLayerNonDistributable,
51    ImageLayerNonDistributableGzip,
55    ImageLayerNonDistributableZstd,
59    ImageConfig,
62    ArtifactManifest,
65    EmptyJSON,
70    Other(String),
72}
73
74impl Display for MediaType {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "{}", self.as_ref())
77    }
78}
79
80impl From<&str> for MediaType {
81    fn from(media_type: &str) -> Self {
82        match media_type {
83            "application/vnd.oci.descriptor" => MediaType::Descriptor,
84            "application/vnd.oci.layout.header.v1+json" => MediaType::LayoutHeader,
85            "application/vnd.oci.image.manifest.v1+json" => MediaType::ImageManifest,
86            "application/vnd.oci.image.index.v1+json" => MediaType::ImageIndex,
87            "application/vnd.oci.image.layer.v1.tar" => MediaType::ImageLayer,
88            "application/vnd.oci.image.layer.v1.tar+gzip" => MediaType::ImageLayerGzip,
89            "application/vnd.oci.image.layer.v1.tar+zstd" => MediaType::ImageLayerZstd,
90            "application/vnd.oci.image.layer.nondistributable.v1.tar" => {
91                MediaType::ImageLayerNonDistributable
92            }
93            "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip" => {
94                MediaType::ImageLayerNonDistributableGzip
95            }
96            "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd" => {
97                MediaType::ImageLayerNonDistributableZstd
98            }
99            "application/vnd.oci.image.config.v1+json" => MediaType::ImageConfig,
100            "application/vnd.oci.artifact.manifest.v1+json" => MediaType::ArtifactManifest,
101            "application/vnd.oci.empty.v1+json" => MediaType::EmptyJSON,
102            media => MediaType::Other(media.to_owned()),
103        }
104    }
105}
106
107impl From<MediaType> for String {
108    fn from(media_type: MediaType) -> Self {
109        media_type.as_ref().to_owned()
110    }
111}
112
113impl AsRef<str> for MediaType {
114    fn as_ref(&self) -> &str {
115        match self {
116            Self::Descriptor => "application/vnd.oci.descriptor",
117            Self::LayoutHeader => "application/vnd.oci.layout.header.v1+json",
118            Self::ImageManifest => "application/vnd.oci.image.manifest.v1+json",
119            Self::ImageIndex => "application/vnd.oci.image.index.v1+json",
120            Self::ImageLayer => "application/vnd.oci.image.layer.v1.tar",
121            Self::ImageLayerGzip => "application/vnd.oci.image.layer.v1.tar+gzip",
122            Self::ImageLayerZstd => "application/vnd.oci.image.layer.v1.tar+zstd",
123            Self::ImageLayerNonDistributable => {
124                "application/vnd.oci.image.layer.nondistributable.v1.tar"
125            }
126            Self::ImageLayerNonDistributableGzip => {
127                "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip"
128            }
129            Self::ImageLayerNonDistributableZstd => {
130                "application/vnd.oci.image.layer.nondistributable.v1.tar+zstd"
131            }
132            Self::ImageConfig => "application/vnd.oci.image.config.v1+json",
133            Self::ArtifactManifest => "application/vnd.oci.artifact.manifest.v1+json",
134            Self::EmptyJSON => "application/vnd.oci.empty.v1+json",
135            Self::Other(media_type) => media_type.as_str(),
136        }
137    }
138}
139
140pub trait ToDockerV2S2 {
148    fn to_docker_v2s2(&self) -> Result<&str, std::fmt::Error>;
151}
152
153impl ToDockerV2S2 for MediaType {
154    fn to_docker_v2s2(&self) -> Result<&str, std::fmt::Error> {
155        Ok(match self {
156            Self::ImageIndex => "application/vnd.docker.distribution.manifest.list.v2+json",
157            Self::ImageManifest => "application/vnd.docker.distribution.manifest.v2+json",
158            Self::ImageConfig => "application/vnd.docker.container.image.v1+json",
159            Self::ImageLayerGzip => "application/vnd.docker.image.rootfs.diff.tar.gzip",
160            _ => return Err(std::fmt::Error),
161        })
162    }
163}
164
165impl Serialize for MediaType {
166    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
167    where
168        S: serde::Serializer,
169    {
170        let media_type = format!("{self}");
171        media_type.serialize(serializer)
172    }
173}
174
175impl<'de> Deserialize<'de> for MediaType {
176    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
177    where
178        D: serde::Deserializer<'de>,
179    {
180        let media_type = String::deserialize(deserializer)?;
181        Ok(media_type.as_str().into())
182    }
183}
184
185#[allow(missing_docs)]
187#[derive(Clone, Debug, PartialEq, Eq)]
188pub enum Os {
189    AIX,
190    Android,
191    Darwin,
192    DragonFlyBSD,
193    FreeBSD,
194    Hurd,
195    Illumos,
196    #[allow(non_camel_case_types)]
197    iOS,
198    Js,
199    Linux,
200    Nacl,
201    NetBSD,
202    OpenBSD,
203    Plan9,
204    Solaris,
205    Windows,
206    #[allow(non_camel_case_types)]
207    zOS,
208    Other(String),
209}
210
211impl From<&str> for Os {
212    fn from(os: &str) -> Self {
213        match os {
214            "aix" => Os::AIX,
215            "android" => Os::Android,
216            "darwin" => Os::Darwin,
217            "dragonfly" => Os::DragonFlyBSD,
218            "freebsd" => Os::FreeBSD,
219            "hurd" => Os::Hurd,
220            "illumos" => Os::Illumos,
221            "ios" => Os::iOS,
222            "js" => Os::Js,
223            "linux" => Os::Linux,
224            "nacl" => Os::Nacl,
225            "netbsd" => Os::NetBSD,
226            "openbsd" => Os::OpenBSD,
227            "plan9" => Os::Plan9,
228            "solaris" => Os::Solaris,
229            "windows" => Os::Windows,
230            "zos" => Os::zOS,
231            name => Os::Other(name.to_owned()),
232        }
233    }
234}
235
236impl Display for Os {
237    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238        let print = match self {
239            Os::AIX => "aix",
240            Os::Android => "android",
241            Os::Darwin => "darwin",
242            Os::DragonFlyBSD => "dragonfly",
243            Os::FreeBSD => "freebsd",
244            Os::Hurd => "hurd",
245            Os::Illumos => "illumos",
246            Os::iOS => "ios",
247            Os::Js => "js",
248            Os::Linux => "linux",
249            Os::Nacl => "nacl",
250            Os::NetBSD => "netbsd",
251            Os::OpenBSD => "openbsd",
252            Os::Plan9 => "plan9",
253            Os::Solaris => "solaris",
254            Os::Windows => "windows",
255            Os::zOS => "zos",
256            Os::Other(name) => name,
257        };
258
259        write!(f, "{print}")
260    }
261}
262
263impl Serialize for Os {
264    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
265    where
266        S: serde::Serializer,
267    {
268        let os = format!("{self}");
269        os.serialize(serializer)
270    }
271}
272
273impl<'de> Deserialize<'de> for Os {
274    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
275    where
276        D: serde::Deserializer<'de>,
277    {
278        let os = String::deserialize(deserializer)?;
279        Ok(os.as_str().into())
280    }
281}
282
283impl Default for Os {
284    fn default() -> Self {
285        Os::from(std::env::consts::OS)
286    }
287}
288
289#[derive(Clone, Debug, PartialEq, Eq)]
291pub enum Arch {
292    #[allow(non_camel_case_types)]
294    i386,
295    Amd64,
297    Amd64p32,
299    ARM,
301    ARMbe,
303    ARM64,
305    ARM64be,
307    LoongArch64,
309    Mips,
311    Mipsle,
313    Mips64,
315    Mips64le,
317    Mips64p32,
319    Mips64p32le,
321    PowerPC,
323    PowerPC64,
325    PowerPC64le,
327    RISCV,
329    RISCV64,
331    #[allow(non_camel_case_types)]
333    s390,
334    #[allow(non_camel_case_types)]
336    s390x,
337    SPARC,
339    SPARC64,
341    Wasm,
343    Other(String),
345}
346
347impl Display for Arch {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        let print = match self {
350            Arch::i386 => "386",
351            Arch::Amd64 => "amd64",
352            Arch::Amd64p32 => "amd64p32",
353            Arch::ARM => "arm",
354            Arch::ARMbe => "armbe",
355            Arch::ARM64 => "arm64",
356            Arch::ARM64be => "arm64be",
357            Arch::LoongArch64 => "loong64",
358            Arch::Mips => "mips",
359            Arch::Mipsle => "mipsle",
360            Arch::Mips64 => "mips64",
361            Arch::Mips64le => "mips64le",
362            Arch::Mips64p32 => "mips64p32",
363            Arch::Mips64p32le => "mips64p32le",
364            Arch::PowerPC => "ppc",
365            Arch::PowerPC64 => "ppc64",
366            Arch::PowerPC64le => "ppc64le",
367            Arch::RISCV => "riscv",
368            Arch::RISCV64 => "riscv64",
369            Arch::s390 => "s390",
370            Arch::s390x => "s390x",
371            Arch::SPARC => "sparc",
372            Arch::SPARC64 => "sparc64",
373            Arch::Wasm => "wasm",
374            Arch::Other(arch) => arch,
375        };
376
377        write!(f, "{print}")
378    }
379}
380
381impl From<&str> for Arch {
382    fn from(arch: &str) -> Self {
383        match arch {
384            "386" => Arch::i386,
385            "amd64" => Arch::Amd64,
386            "amd64p32" => Arch::Amd64p32,
387            "arm" => Arch::ARM,
388            "armbe" => Arch::ARM64be,
389            "arm64" => Arch::ARM64,
390            "arm64be" => Arch::ARM64be,
391            "loong64" => Arch::LoongArch64,
392            "mips" => Arch::Mips,
393            "mipsle" => Arch::Mipsle,
394            "mips64" => Arch::Mips64,
395            "mips64le" => Arch::Mips64le,
396            "mips64p32" => Arch::Mips64p32,
397            "mips64p32le" => Arch::Mips64p32le,
398            "ppc" => Arch::PowerPC,
399            "ppc64" => Arch::PowerPC64,
400            "ppc64le" => Arch::PowerPC64le,
401            "riscv" => Arch::RISCV,
402            "riscv64" => Arch::RISCV64,
403            "s390" => Arch::s390,
404            "s390x" => Arch::s390x,
405            "sparc" => Arch::SPARC,
406            "sparc64" => Arch::SPARC64,
407            "wasm" => Arch::Wasm,
408            arch => Arch::Other(arch.to_owned()),
409        }
410    }
411}
412
413impl Serialize for Arch {
414    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
415    where
416        S: serde::Serializer,
417    {
418        let arch = format!("{self}");
419        arch.serialize(serializer)
420    }
421}
422
423impl<'de> Deserialize<'de> for Arch {
424    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
425    where
426        D: serde::Deserializer<'de>,
427    {
428        let arch = String::deserialize(deserializer)?;
429        Ok(arch.as_str().into())
430    }
431}
432
433impl Default for Arch {
434    fn default() -> Self {
435        let goarch = match std::env::consts::ARCH {
440            "x86_64" => "amd64",
441            "aarch64" => "arm64",
442            "powerpc64" if cfg!(target_endian = "big") => "ppc64",
443            "powerpc64" if cfg!(target_endian = "little") => "ppc64le",
444            o => o,
445        };
446        Arch::from(goarch)
447    }
448}
449
450#[cfg(test)]
451mod tests {
452    use super::*;
453
454    #[test]
455    fn test_arch_translation() {
456        let a = Arch::default();
457        if let Arch::Other(o) = a {
459            panic!("Architecture {o} not mapped between Rust and OCI")
460        }
461    }
462
463    #[test]
464    fn test_asref() {
465        assert_eq!(
467            MediaType::ImageConfig.as_ref(),
468            "application/vnd.oci.image.config.v1+json"
469        );
470        assert_eq!(
471            String::from(MediaType::ImageConfig).as_str(),
472            "application/vnd.oci.image.config.v1+json"
473        );
474    }
475}