containerization-framework 0.4.0

Rust bindings for Apple's Containerization framework: Linux containers in lightweight VMs, in-process and without a daemon.
//! Rust bindings for Apple's [Containerization] framework: Linux containers,
//! each in its own lightweight VM, in-process and with no daemon.
//!
//! # Shape
//!
//! The Rust API mirrors Containerization's Swift API, as much as possible.
//! Modules are named after the Swift modules, types after the Swift types, and
//! methods after their Swift methods, written in Rust's snake case. Within a
//! module, the types are grouped by what they work on, so `ImageStore` is in
//! `containerization::image` and `LinuxContainer` is in
//! `containerization::container`. A Swift type nested in another, like
//! `LinuxContainer.Configuration`, is found in a module named after its parent,
//! in the parent's group: `container::linux_container::Configuration`.
//!
//! Swift's `async` methods block until they finish, and errors they throw are
//! returned as [`Error`].
//!
//! ```no_run
//! use containerization_framework as cfw;
//! use cfw::containerization as cz;
//! # fn main() -> Result<(), cfw::Error> {
//! let store = cz::image::ImageStore::new("/path/to/store".as_ref())?;
//! let kernel = cz::vm::Kernel::new("/path/to/vmlinux", cz::vm::SystemPlatform::LINUX_ARM);
//! let mut manager = cz::container::ContainerManager::with_initfs_reference(
//!   &kernel,
//!   "ghcr.io/apple/containerization/vminit:0.48.0",
//!   &store,
//!   Default::default(),
//! )?;
//! let image = store.get("docker.io/library/alpine:3", true)?;
//! let options = cz::container::container_manager::CreateOptions {
//!   networking: false,
//!   ..Default::default()
//! };
//! let container = manager.create("demo", &image, options, |config| {
//!   config.process.arguments = vec!["/bin/sleep".into(), "infinity".into()];
//! })?;
//! container.create()?;
//! container.start()?;
//! let process = container.exec("ls", cz::process::LinuxProcessConfiguration::new(&["/bin/ls", "/"]))?;
//! process.start()?;
//! let status = process.wait(None)?;
//! process.delete()?;
//! container.stop()?;
//! manager.delete("demo")?;
//! # let _ = status;
//! # Ok(())
//! # }
//! ```
//!
//! # Requirements
//!
//! macOS 26 on Apple silicon, with Xcode 26 to build. The Swift package is
//! compiled by this crate's build script, which resolves Containerization and
//! its dependencies from the network on a first build.
//!
//! **A binary using this crate must be codesigned with the
//! `com.apple.security.virtualization` entitlement** to create a container. A
//! plain `cargo build` drops the signature, so re-sign after each one;
//! `containerization.entitlements` in this crate is the file to pass to
//! `codesign --entitlements`.
//!
//! Elsewhere this crate compiles, and every constructor fails with
//! [`Error::Unavailable`], so a cross-platform workspace still builds.
//!
//! [Containerization]: https://github.com/apple/containerization

/// A Swift enum backed by strings: Rust's cases, each with its `rawValue`.
macro_rules! raw_values {
  ($(#[$meta:meta])* $name:ident { $($case:ident = $raw:literal),* $(,)? }) => {
    $(#[$meta])*
    #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
    pub enum $name {
      $(#[doc = concat!("`", $raw, "`.")] $case),*
    }

    impl $name {
      /// Every case, in the order the bridge lists Swift's raw values.
      #[cfg(all(test, target_os = "macos"))]
      pub(crate) const ALL: &[Self] = &[$(Self::$case),*];

      /// `rawValue`.
      pub fn raw_value(self) -> &'static str {
        match self {
          $(Self::$case => $raw),*
        }
      }

      /// `init?(rawValue:)`.
      pub fn from_raw_value(raw_value: &str) -> Option<Self> {
        match raw_value {
          $($raw => Some(Self::$case),)*
          _ => None,
        }
      }

      /// A raw value from Swift. It panics if Swift has a case Rust lacks.
      /// Not every enum uses it.
      #[allow(dead_code)]
      pub(crate) fn from_swift(raw_value: &str) -> Self {
        Self::from_raw_value(raw_value)
          .unwrap_or_else(|| panic!("Swift's {} has a case Rust lacks: {raw_value:?}", stringify!($name)))
      }
    }
  };
}

pub mod containerization;
pub mod containerization_archive;
pub mod containerization_error;
pub mod containerization_ext4;
pub mod containerization_extras;
pub mod containerization_io;
pub mod containerization_oci;
pub mod containerization_os;
pub mod error;

mod platform;

// The bridge's functions take Swift's arguments, however many there are.
#[cfg(target_os = "macos")]
#[allow(clippy::too_many_arguments)]
mod bridge;

pub use crate::error::Error;