containerization-framework 0.10.0

Rust bindings for Apple's Containerization framework: Linux containers in lightweight VMs, in-process and without a daemon.
//===----------------------------------------------------------------------===//
// The address types: Rust's read into ContainerizationExtras' and
// Containerization's, and Swift's read back by Rust field by field. The Rust
// side is `bridge::accessors::addresses`.
//
// IPv4 and MAC addresses cross as their values, and a prefix as its length.
// IPv6 and IP addresses cross as opaque Rust types.
//===----------------------------------------------------------------------===//

import Containerization
import ContainerizationExtras
import Foundation

// MARK: Rust to Swift

extension IPv6Address {
  init(_ address: RustIPv6AddressRef) {
    self.init(UInt128(address.valueHigh()) << 64 | UInt128(address.valueLow()), zone: address.zone()?.toString())
  }
}

extension IPAddress {
  init(_ address: RustIpAddressRef) {
    self = address.holdsV6() ? .v6(IPv6Address(address.v6())) : .v4(IPv4Address(address.v4Value()))
  }
}

extension Prefix {
  /// A length Rust holds, which Swift made.
  init(bridged length: UInt8) throws {
    guard let prefix = Prefix(length: length) else {
      throw BridgeError.malformed("prefix length", String(length))
    }
    self = prefix
  }
}

extension CIDRv4 {
  init(bridged address: UInt32, prefix: UInt8) throws {
    try self.init(IPv4Address(address), prefix: Prefix(bridged: prefix))
  }
}

extension CIDRv6 {
  init(bridged address: RustIPv6AddressRef, prefix: UInt8) throws {
    try self.init(IPv6Address(address), prefix: Prefix(bridged: prefix))
  }
}

extension CIDR {
  /// The case Rust holds, made directly as Rust's was.
  init(bridged address: RustIpAddressRef, prefix: UInt8) throws {
    let prefix = try Prefix(bridged: prefix)
    self =
      switch IPAddress(address) {
      case .v4(let address): .v4(address, prefix)
      case .v6(let address): .v6(address, prefix)
      }
  }
}

extension InterfaceAddress {
  init(_ address: RustInterfaceAddressRef) throws {
    self.init(
      ipv4Address: try CIDRv4(bridged: address.ipv4AddressValue(), prefix: address.ipv4AddressPrefix()),
      ipv6Address: address.hasIpv6Address()
        ? try CIDRv6(bridged: address.ipv6AddressAddress(), prefix: address.ipv6AddressPrefix())
        : nil
    )
  }
}

extension LinkRoute {
  init(_ route: RustLinkRouteRef) {
    self.init(
      ipv4Destination: route.ipv4Destination().map { IPv4Address($0) },
      ipv4Source: route.ipv4Source().map { IPv4Address($0) },
      ipv6Destination: route.hasIpv6Destination() ? IPv6Address(route.ipv6Destination()) : nil,
      ipv6Source: route.hasIpv6Source() ? IPv6Address(route.ipv6Source()) : nil
    )
  }
}

extension DefaultRoute {
  init(_ route: RustDefaultRouteRef) {
    self.init(
      ipv4Gateway: route.ipv4Gateway().map { IPv4Address($0) },
      ipv6Gateway: route.hasIpv6Gateway() ? IPv6Address(route.ipv6Gateway()) : nil
    )
  }
}

// MARK: Swift to Rust

extension CzOutcome {
  /// Whether the address held, alone or in a CIDR block, is IPv6.
  func isIPv6() -> Bool {
    heldAddress() is IPv6Address
  }

  func ipv4Value() -> UInt32 {
    (cast(heldAddress()) as IPv4Address).value
  }

  func ipv6High() -> UInt64 {
    UInt64((cast(heldAddress()) as IPv6Address).value >> 64)
  }

  func ipv6Low() -> UInt64 {
    UInt64(truncatingIfNeeded: (cast(heldAddress()) as IPv6Address).value)
  }

  func ipv6Zone() -> String? {
    (cast(heldAddress()) as IPv6Address).zone
  }

  func prefixLength() -> UInt8 {
    switch value {
    case let cidr as CIDRv4: cidr.prefix.length
    case let cidr as CIDRv6: cidr.prefix.length
    case let cidr as CIDR: cidr.prefix.length
    default: (taken() as Prefix).length
    }
  }

  func macValue() -> UInt64 {
    (taken() as MACAddress).value
  }

  /// The `IPv4Address` or `IPv6Address` held, alone or in an `IPAddress`, a
  /// CIDR block or a `CIDR`.
  private func heldAddress() -> Any? {
    switch value {
    case let cidr as CIDRv4: cidr.address
    case let cidr as CIDRv6: cidr.address
    case let cidr as CIDR: unwrapped(cidr.address)
    case let address as IPAddress: unwrapped(address)
    default: value
    }
  }

  private func unwrapped(_ address: IPAddress) -> Any {
    switch address {
    case .v4(let address): address
    case .v6(let address): address
    }
  }
}