#[cfg(not(target_os = "macos"))]
#[allow(clippy::too_many_arguments)]
pub(crate) mod unsupported;
#[cfg(target_os = "macos")]
pub(crate) use crate::bridge::ffi;
#[cfg(not(target_os = "macos"))]
pub(crate) use self::unsupported as ffi;
use crate::containerization::container;
use crate::containerization::process;
#[cfg(target_os = "macos")]
use crate::containerization_error;
use crate::containerization_extras::ProgressHandler;
use crate::error::Error;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::PoisonError;
use std::time::Duration;
use std::time::SystemTime;
pub(crate) struct Progress(pub(crate) Option<ProgressHandler>);
type Configuring<T> = Box<dyn FnOnce(&mut T) + Send>;
pub(crate) struct Configure<T>(Mutex<Option<Configuring<T>>>);
impl<T> Configure<T> {
pub(crate) fn new(configure: impl FnOnce(&mut T) + Send + 'static) -> Self {
Self(Mutex::new(Some(Box::new(configure))))
}
pub(crate) fn call(&self, value: &mut T) {
let configure = self.0.lock().unwrap_or_else(PoisonError::into_inner).take();
if let Some(configure) = configure {
configure(value);
}
}
}
pub(crate) type ConfigureContainer = Configure<container::linux_container::Configuration>;
pub(crate) type ConfigureProcess = Configure<process::LinuxProcessConfiguration>;
pub(crate) type ConfigurePod = Configure<container::linux_pod::Configuration>;
pub(crate) type ConfigurePodContainer = Configure<container::linux_pod::ContainerConfiguration>;
pub(crate) fn filled<T: Default + Send + 'static, R>(call: impl FnOnce(Configure<T>) -> R) -> (R, Option<T>) {
let filled = Arc::new(Mutex::new(None));
let slot = Arc::clone(&filled);
let receive = Configure::new(move |value| {
*slot.lock().unwrap_or_else(PoisonError::into_inner) = Some(std::mem::take(value));
});
let returned = call(receive);
let value = filled.lock().unwrap_or_else(PoisonError::into_inner).take();
(returned, value)
}
pub(crate) fn filled_process(
call: impl FnOnce(ConfigureProcess) -> ffi::CzOutcome,
action: impl Into<String>,
) -> Result<process::LinuxProcessConfiguration, Error> {
let (returned, process) = filled(call);
outcome(returned, action)?;
Ok(process.expect("Swift hands back the configuration it fills"))
}
pub(crate) fn outcome(outcome: ffi::CzOutcome, action: impl Into<String>) -> Result<ffi::CzOutcome, Error> {
match outcome.error() {
Some(message) => Err(error(&outcome, action, message)),
None => Ok(outcome),
}
}
#[cfg(target_os = "macos")]
fn error(outcome: &ffi::CzOutcome, action: impl Into<String>, message: String) -> Error {
let code = outcome
.error_code()
.and_then(|code| containerization_error::Code::from_description(&code));
Error::failed_with_code(action, message, code)
}
#[cfg(not(target_os = "macos"))]
fn error(_outcome: &ffi::CzOutcome, action: impl Into<String>, message: String) -> Error {
Error::unavailable(action, message)
}
pub(crate) fn exit_status(outcome: &ffi::CzOutcome) -> process::ExitStatus {
process::ExitStatus {
exit_code: outcome.exit_code(),
exited_at: SystemTime::UNIX_EPOCH + Duration::from_secs_f64(outcome.exited_at().max(0.0)),
}
}
pub(crate) fn system_time(seconds: f64) -> SystemTime {
if seconds < 0.0 {
SystemTime::UNIX_EPOCH - Duration::from_secs_f64(-seconds)
} else {
SystemTime::UNIX_EPOCH + Duration::from_secs_f64(seconds)
}
}
pub(crate) unsafe trait Plain: Copy {}
pub(crate) fn from_bytes<T: Plain>(bytes: &[u8]) -> T {
assert_eq!(
bytes.len(),
std::mem::size_of::<T>(),
"Swift's {} is a different size from Rust's",
std::any::type_name::<T>()
);
unsafe { bytes.as_ptr().cast::<T>().read_unaligned() }
}
pub(crate) fn seconds(time: SystemTime) -> f64 {
match time.duration_since(SystemTime::UNIX_EPOCH) {
Ok(after) => after.as_secs_f64(),
Err(before) => -before.duration().as_secs_f64(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crosses_dates_on_either_side_of_1970() {
for seconds in [-86_400.5, 0.0, 1_700_000_000.25] {
assert_eq!(super::seconds(system_time(seconds)), seconds);
}
}
}