ocipkg/
lib.rs

1//! ocipkg consists of executables (`ocipkg` and `cargo-ocipkg`) and this crate for handling OCI Artifact without container runtime.
2//!
3//! See [README.md at GitHub](https://github.com/termoshtt/ocipkg) for usage of the executables.
4//! This reference describes the crate part.
5//!
6//! Layout, Manifest, and Artifact
7//! -------------------------------
8//! ocipkg defines original type of OCI Artifact, `application/vnd.ocipkg.v1.artifact` to exchange directories containing static libraries.
9//! But this crate is also a general purpose library to handle any kind of OCI Artifact which can contain any kind of data.
10//!
11//! Note that current implementation (from ocipkg 0.3.0) is based on [OCI Image specification] 1.1.0.
12//!
13//! ### Image Manifest
14//! Every contents in a container are stored as blob, and identified by its hash digest (usually SHA256 is used).
15//! [OCI Image Manifest] describes how these blobs are combined to form a container.
16//! From [OCI Image specification] 1.1.0, [OCI Image Manifest] can store OCI Artifact in addition to usual executable containers.
17//!
18//! In this crate, [oci_spec::image::ImageManifest] is used to represent [OCI Image Manifest].
19//!
20//! ### Image Layout
21//! [OCI Image Layout] specifies how blobs are stored as a directory.
22//! Blobs are stored in `blobs/sha256/` directory with its hash digest as a file name.
23//! [OCI Image Manifest] is a JSON string and also stored as a blob, thus we have to find the blob storing the manifest first.
24//! `index.json` lists the digests of manifests stored in the layout.
25//! There is also a file `oci-layout` which contains version of the layout itself.
26//!
27//! Two types of layout formats are supported. `oci-dir` format is a directory containing blobs as [OCI Image Layout] format:
28//!
29//! ```text
30//! {dirname}/
31//! ├── blobs/
32//! │   └── sha256/
33//! │       ├── 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
34//! │       └── ...
35//! ├── index.json
36//! └── oci-layout
37//! ```
38//!
39//! `oci-archive` format is a tar archive of a `oci-dir` format. Creating a new image layout takes following steps:
40//!
41//! 1. Create an empty layout
42//! 2. Add blobs, and store their digests
43//! 3. Create a manifest using blob digests, store the manifest itself as blob, and store its digest in `index.json`
44//!
45//! This process is abstracted by [image::ImageBuilder]. This yields a layout which implements [image::Image] trait.
46//!
47//! [OCI Image specification]: https://github.com/opencontainers/image-spec/blob/v1.1.0/spec.md
48//! [OCI Image Manifest]: https://github.com/opencontainers/image-spec/blob/v1.1.0/manifest.md
49//! [OCI Image Layout]: https://github.com/opencontainers/image-spec/blob/v1.1.0/image-layout.md
50
51/// Re-export since this crate exposes types in `oci_spec` crate.
52pub extern crate oci_spec;
53
54#[cfg(feature = "remote")]
55pub mod distribution;
56pub mod image;
57pub mod local;
58pub mod media_types;
59
60mod digest;
61mod image_name;
62mod name;
63mod reference;
64
65pub use image_name::ImageName;
66pub use name::Name;
67pub use oci_spec::image::Digest;
68pub use reference::Reference;
69
70#[cfg(feature = "remote")]
71mod link_support {
72    use crate::{distribution, local, ImageName};
73    use anyhow::{Context, Result};
74    use std::fs;
75
76    const STATIC_PREFIX: &str = if cfg!(target_os = "windows") {
77        ""
78    } else {
79        "lib"
80    };
81
82    const STATIC_EXTENSION: &str = if cfg!(target_os = "windows") {
83        "lib"
84    } else {
85        "a"
86    };
87
88    /// Get and link package in `build.rs` with [cargo link instructions](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script).
89    ///
90    /// This is aimed to use in [build script](https://doc.rust-lang.org/cargo/reference/build-scripts.html) a.k.a. `build.rs`.
91    pub fn link_package(image_name: &str) -> Result<()> {
92        let image_name = ImageName::parse(image_name)?;
93        let dir = local::image_dir(&image_name)?;
94        if !dir.exists() {
95            distribution::get_image(&image_name, false)?;
96        }
97        println!("cargo:rustc-link-search={}", dir.display());
98        for path in fs::read_dir(&dir)?.filter_map(|entry| {
99            let path = entry.ok()?.path();
100            path.is_file().then_some(path)
101        }) {
102            let name = path
103                .file_stem()
104                .unwrap()
105                .to_str()
106                .context("Non UTF-8 path is not supported")?;
107            let name = if let Some(name) = name.strip_prefix(STATIC_PREFIX) {
108                name
109            } else {
110                continue;
111            };
112            if let Some(ext) = path.extension() {
113                if ext == STATIC_EXTENSION {
114                    println!("cargo:rustc-link-lib=static={}", name);
115                }
116            }
117        }
118        println!("cargo:rerun-if-changed={}", dir.display());
119        println!("cargo:rerun-if-env-changed=XDG_DATA_HOME");
120        Ok(())
121    }
122}
123
124#[cfg(feature = "remote")]
125pub use link_support::link_package;