containerization-framework 0.4.0

Rust bindings for Apple's Containerization framework: Linux containers in lightweight VMs, in-process and without a daemon.
//===----------------------------------------------------------------------===//
// ContainerizationOS: `Terminal`, `CapabilityName`, `CapabilitySet`,
// `KeychainQuery`, `Sysctl` and `File`.
//
// `CapabilityName` and `CapabilitySet` cross as their `description`s.
//===----------------------------------------------------------------------===//

import ContainerizationOS
import Foundation

/// For a unit test: `CapabilityName.allCases`' descriptions.
func capabilityNameDescriptions() -> RustVec<RustString> {
  rustStrings(CapabilityName.allCases.map(\.description))
}

/// For a unit test: `CapabilityName.allCases`' `capValue`s.
func capabilityNameCapValues() -> RustVec<UInt32> {
  let vec = RustVec<UInt32>()
  for name in CapabilityName.allCases {
    vec.push(value: name.capValue)
  }
  return vec
}

/// For a unit test: each `CapabilitySet`'s description, in the order of Rust's
/// `ALL`.
func capabilitySetDescriptions() -> RustVec<RustString> {
  rustStrings(
    [ContainerizationOS.CapabilitySet.bounding, .effective, .inheritable, .permitted, .ambient].map(\.description))
}

func parseCapabilityName(rawValue: RustStr) -> CzOutcome {
  let rawValue = rawValue.toString()

  return CzOutcome { try CapabilityName(rawValue: rawValue).description }
}

func parseCapabilitySet(rawValue: RustStr) -> CzOutcome {
  let rawValue = rawValue.toString()

  // Qualified: the bridge has a `CapabilitySet` of its own.
  return CzOutcome { try ContainerizationOS.CapabilitySet(rawValue: rawValue).description }
}

func newTerminal(descriptor: Int32, setInitState: Bool) -> CzOutcome {
  CzOutcome { CzTerminal(try Terminal(descriptor: descriptor, setInitState: setInitState)) }
}

func currentTerminal() -> CzOutcome {
  CzOutcome { CzTerminal(try Terminal.current) }
}

func createTerminal(hasInitialSize: Bool, width: UInt16, height: UInt16) -> CzOutcome {
  let initialSize = hasInitialSize ? Terminal.Size(width: width, height: height) : nil

  return CzOutcome { try Terminal.create(initialSize: initialSize) }
}

/// A `Terminal`.
final class CzTerminal: Sendable {
  let terminal: Terminal

  init(_ terminal: Terminal) {
    self.terminal = terminal
  }

  /// A copy of the terminal, for a call to take.
  func duplicate() -> CzTerminal {
    CzTerminal(terminal)
  }

  func handle() -> Int32 {
    terminal.handle.fileDescriptor
  }

  func write(data: RustVec<UInt8>) -> CzOutcome {
    let data = Data(data)

    return CzOutcome { try terminal.write(data) }
  }

  func size() -> CzOutcome {
    CzOutcome { try terminal.size }
  }

  func resizeFrom(pty: CzTerminal) -> CzOutcome {
    CzOutcome { try terminal.resize(from: pty.terminal) }
  }

  func resizeSize(width: UInt16, height: UInt16) -> CzOutcome {
    CzOutcome { try terminal.resize(size: Terminal.Size(width: width, height: height)) }
  }

  func resize(width: UInt16, height: UInt16) -> CzOutcome {
    CzOutcome { try terminal.resize(width: width, height: height) }
  }

  func setraw() -> CzOutcome {
    CzOutcome { try terminal.setraw() }
  }

  func enableEcho() -> CzOutcome {
    CzOutcome { try terminal.enableEcho() }
  }

  func disableEcho() -> CzOutcome {
    CzOutcome { try terminal.disableEcho() }
  }

  func close() -> CzOutcome {
    CzOutcome { try terminal.close() }
  }

  func reset() -> CzOutcome {
    CzOutcome { try terminal.reset() }
  }

  func tryReset() {
    terminal.tryReset()
  }
}

func keychainQuerySave(
  securityDomain: RustStr,
  accessGroup: RustString?,
  hostname: RustStr,
  username: RustStr,
  password: RustStr
) -> CzOutcome {
  let securityDomain = securityDomain.toString()
  let accessGroup = accessGroup?.toString()
  let hostname = hostname.toString()
  let username = username.toString()
  let password = password.toString()

  return CzOutcome {
    try KeychainQuery().save(
      securityDomain: securityDomain,
      accessGroup: accessGroup,
      hostname: hostname,
      username: username,
      password: password
    )
  }
}

func keychainQueryDelete(securityDomain: RustStr, accessGroup: RustString?, hostname: RustStr) -> CzOutcome {
  let securityDomain = securityDomain.toString()
  let accessGroup = accessGroup?.toString()
  let hostname = hostname.toString()

  return CzOutcome {
    try KeychainQuery().delete(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname)
  }
}

func keychainQueryGet(securityDomain: RustStr, accessGroup: RustString?, hostname: RustStr) -> CzOutcome {
  let securityDomain = securityDomain.toString()
  let accessGroup = accessGroup?.toString()
  let hostname = hostname.toString()

  return CzOutcome {
    absent(try KeychainQuery().get(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname))
  }
}

func keychainQueryList(securityDomain: RustStr, accessGroup: RustString?) -> CzOutcome {
  let securityDomain = securityDomain.toString()
  let accessGroup = accessGroup?.toString()

  return CzOutcome { try KeychainQuery().list(securityDomain: securityDomain, accessGroup: accessGroup) }
}

func keychainQueryExists(securityDomain: RustStr, accessGroup: RustString?, hostname: RustStr) -> CzOutcome {
  let securityDomain = securityDomain.toString()
  let accessGroup = accessGroup?.toString()
  let hostname = hostname.toString()

  return CzOutcome {
    try KeychainQuery().exists(securityDomain: securityDomain, accessGroup: accessGroup, hostname: hostname)
  }
}

func sysctlByName(name: RustStr) -> CzOutcome {
  let name = name.toString()

  return CzOutcome { try Sysctl.byName(name) }
}

func fileInfoAt(path: RustStr) -> CzOutcome {
  let path = path.toString()

  return CzOutcome { CzFileInfo(try File.info(path)) }
}

/// A `FileInfo`.
final class CzFileInfo: Sendable {
  let info: FileInfo

  init(_ info: FileInfo) {
    self.info = info
  }

  func mode() -> UInt16 { info.mode }
  func uid() -> Int64 { Int64(info.uid) }
  func gid() -> Int64 { Int64(info.gid) }
  func dev() -> Int64 { Int64(info.dev) }
  func ino() -> Int64 { Int64(info.ino) }
  func size() -> Int64 { Int64(info.size) }
  func path() -> String { info.path }
  func isDirectory() -> Bool { info.isDirectory }
  func isPipe() -> Bool { info.isPipe }
  func isSocket() -> Bool { info.isSocket }
  func isLink() -> Bool { info.isLink }
  func isRegularFile() -> Bool { info.isRegularFile }
  func isBlock() -> Bool { info.isBlock }
  func isChar() -> Bool { info.isChar }
}

extension CzOutcome {
  func terminal() -> CzTerminal { taken() }
  func fileInfo() -> CzFileInfo { taken() }
  func integer() -> Int64 { taken() }

  func parentTerminal() -> CzTerminal {
    CzTerminal((taken() as (parent: Terminal, child: Terminal)).parent)
  }

  func childTerminal() -> CzTerminal {
    CzTerminal((taken() as (parent: Terminal, child: Terminal)).child)
  }

  func terminalSizeWidth() -> UInt16 {
    (taken() as Terminal.Size).width
  }

  func terminalSizeHeight() -> UInt16 {
    (taken() as Terminal.Size).height
  }

  // A `KeychainQueryResult`, field by field: Rust builds its own.

  func keychainQueryResultUsername() -> String {
    (taken() as KeychainQueryResult).username
  }

  func keychainQueryResultPassword() -> String {
    (taken() as KeychainQueryResult).password
  }

  func keychainQueryResultModifiedDate() -> Double {
    (taken() as KeychainQueryResult).modifiedDate.timeIntervalSince1970
  }

  func keychainQueryResultCreatedDate() -> Double {
    (taken() as KeychainQueryResult).createdDate.timeIntervalSince1970
  }
}