vmnet 0.5.1

Apple's vmnet.framework bindings
Documentation
use crate::ffi::xpc::{
    xpc_dictionary_apply, xpc_dictionary_create_empty, xpc_dictionary_get_value,
    xpc_dictionary_set_bool, xpc_dictionary_set_string, xpc_dictionary_set_uint64,
    xpc_dictionary_set_uuid, xpc_release, xpc_retain, XpcData, XpcObjectT,
};
use block::ConcreteBlock;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::rc::Rc;

pub struct Dictionary {
    xdict: XpcObjectT,
}

impl Dictionary {
    pub fn new() -> Self {
        Dictionary {
            xdict: unsafe { xpc_dictionary_create_empty() },
        }
    }

    pub unsafe fn from_xpc(xdict: XpcObjectT) -> Self {
        Dictionary {
            xdict: xpc_retain(xdict),
        }
    }

    pub unsafe fn to_xpc(&self) -> XpcObjectT {
        self.xdict
    }

    pub fn get(&self, key: String) -> Option<XpcData> {
        let key_ = CString::new(key).unwrap();
        let key = key_.as_ptr();

        let xpc_value = unsafe { xpc_dictionary_get_value(self.xdict, key) };
        if xpc_value.is_null() {
            return None;
        }

        unsafe { XpcData::from_xpc_value(xpc_value) }
    }

    pub fn set(&mut self, key: String, value: XpcData) {
        let key_ = CString::new(key).unwrap();
        let key = key_.as_ptr();

        match value {
            XpcData::Uint64(value) => unsafe { xpc_dictionary_set_uint64(self.xdict, key, value) },
            XpcData::String(value) => {
                let c_string = CString::new(value).unwrap();
                unsafe { xpc_dictionary_set_string(self.xdict, key, c_string.as_ptr()) }
            }
            XpcData::Bool(value) => unsafe { xpc_dictionary_set_bool(self.xdict, key, value) },
            XpcData::Uuid(value) => unsafe {
                xpc_dictionary_set_uuid(self.xdict, key, value.as_bytes().as_ptr())
            },
        };
    }
}

impl Default for Dictionary {
    fn default() -> Self {
        Self::new()
    }
}

impl Clone for Dictionary {
    fn clone(&self) -> Self {
        Dictionary {
            xdict: unsafe { xpc_retain(self.xdict) },
        }
    }
}

impl Drop for Dictionary {
    fn drop(&mut self) {
        unsafe { xpc_release(self.xdict) }
    }
}

impl From<HashMap<String, XpcData>> for Dictionary {
    fn from(from: HashMap<String, XpcData>) -> Self {
        let mut result = Dictionary::new();

        for (key, value) in from {
            result.set(key, value);
        }

        result
    }
}

impl TryFrom<Dictionary> for HashMap<String, XpcData> {
    type Error = ();

    fn try_from(value: Dictionary) -> Result<Self, Self::Error> {
        let result = Rc::new(RefCell::new(HashMap::new()));
        let result_weak = Rc::downgrade(&result);

        let block = ConcreteBlock::new(move |key: *const c_char, value: XpcObjectT| -> bool {
            let key = unsafe { CStr::from_ptr(key).to_string_lossy().to_string() };

            let value = match unsafe { XpcData::from_xpc_value(value) } {
                Some(value) => value,
                None => return false,
            };

            result_weak
                .upgrade()
                .unwrap()
                .borrow_mut()
                .insert(key, value);

            true
        });
        let block = block.copy();

        let ret = unsafe { xpc_dictionary_apply(value.xdict, &*block as *const _ as *mut _) };
        if !ret {
            return Err(());
        }

        Ok(Rc::try_unwrap(result).unwrap().into_inner())
    }
}