systemconfiguration-rs 0.2.1

Safe Rust bindings for Apple's SystemConfiguration framework via a Swift bridge on macOS
Documentation
use serde::Deserialize;

use crate::{bridge, error::Result, ffi, PropertyList, SystemConfigurationError};

#[derive(Clone, Debug)]
pub struct NetworkInterface {
    raw: bridge::OwnedHandle,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct NetworkInterfaceMtuInfo {
    pub current: i32,
    pub minimum: i32,
    pub maximum: i32,
}

#[derive(Clone, Debug)]
pub struct NetworkInterfaceMediaOptions {
    pub current: Option<PropertyList>,
    pub active: Option<PropertyList>,
    pub available: Option<PropertyList>,
}

impl NetworkInterface {
    pub fn type_id() -> u64 {
        unsafe { ffi::network_interface::sc_network_interface_get_type_id() }
    }

    pub fn copy_all() -> Result<Vec<Self>> {
        let raw = unsafe { ffi::network_interface::sc_network_interface_copy_all() };
        if raw.is_null() {
            return Err(SystemConfigurationError::last("sc_network_interface_copy_all"));
        }
        Ok(bridge::take_handle_array(raw, Self::from_owned_handle))
    }

    pub fn ipv4() -> Result<Self> {
        let raw = unsafe { ffi::network_interface::sc_network_interface_copy_ipv4() };
        let raw = bridge::owned_handle_or_last("sc_network_interface_copy_ipv4", raw)?;
        Ok(Self::from_owned_handle(raw))
    }

    pub fn supported_interface_types(&self) -> Vec<String> {
        bridge::take_string_array(unsafe {
            ffi::network_interface::sc_network_interface_copy_supported_interface_types(self.raw.as_ptr())
        })
    }

    pub fn supported_protocol_types(&self) -> Vec<String> {
        bridge::take_string_array(unsafe {
            ffi::network_interface::sc_network_interface_copy_supported_protocol_types(self.raw.as_ptr())
        })
    }

    pub fn create_layered_interface(&self, interface_type: &str) -> Result<Option<Self>> {
        let interface_type = bridge::cstring(interface_type, "sc_network_interface_create_with_interface")?;
        let raw = unsafe {
            ffi::network_interface::sc_network_interface_create_with_interface(
                self.raw.as_ptr(),
                interface_type.as_ptr(),
            )
        };
        Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(Self::from_owned_handle))
    }

    pub fn bsd_name(&self) -> Result<Option<String>> {
        Ok(bridge::take_optional_string(unsafe {
            ffi::network_interface::sc_network_interface_copy_bsd_name(self.raw.as_ptr())
        }))
    }

    pub fn configuration(&self) -> Option<PropertyList> {
        unsafe { bridge::OwnedHandle::from_raw(ffi::network_interface::sc_network_interface_copy_configuration(self.raw.as_ptr())) }
            .map(PropertyList::from_owned_handle)
    }

    pub fn extended_configuration(&self, extended_type: &str) -> Result<Option<PropertyList>> {
        let extended_type = bridge::cstring(extended_type, "sc_network_interface_copy_extended_configuration")?;
        let raw = unsafe {
            ffi::network_interface::sc_network_interface_copy_extended_configuration(
                self.raw.as_ptr(),
                extended_type.as_ptr(),
            )
        };
        Ok(unsafe { bridge::OwnedHandle::from_raw(raw) }.map(PropertyList::from_owned_handle))
    }

    pub fn hardware_address_string(&self) -> Result<Option<String>> {
        Ok(bridge::take_optional_string(unsafe {
            ffi::network_interface::sc_network_interface_copy_hardware_address(self.raw.as_ptr())
        }))
    }

    pub fn underlying_interface(&self) -> Option<Self> {
        unsafe {
            bridge::OwnedHandle::from_raw(ffi::network_interface::sc_network_interface_copy_underlying_interface(
                self.raw.as_ptr(),
            ))
        }
        .map(Self::from_owned_handle)
    }

    pub fn interface_type(&self) -> Result<Option<String>> {
        Ok(bridge::take_optional_string(unsafe {
            ffi::network_interface::sc_network_interface_copy_interface_type(self.raw.as_ptr())
        }))
    }

    pub fn localized_display_name(&self) -> Result<Option<String>> {
        Ok(bridge::take_optional_string(unsafe {
            ffi::network_interface::sc_network_interface_copy_localized_display_name(self.raw.as_ptr())
        }))
    }

    pub fn media_options(&self, filter: bool) -> NetworkInterfaceMediaOptions {
        let current = unsafe {
            bridge::OwnedHandle::from_raw(ffi::network_interface::sc_network_interface_copy_media_options_current(
                self.raw.as_ptr(),
                u8::from(filter),
            ))
        }
        .map(PropertyList::from_owned_handle);
        let active = unsafe {
            bridge::OwnedHandle::from_raw(ffi::network_interface::sc_network_interface_copy_media_options_active(
                self.raw.as_ptr(),
                u8::from(filter),
            ))
        }
        .map(PropertyList::from_owned_handle);
        let available = unsafe {
            bridge::OwnedHandle::from_raw(ffi::network_interface::sc_network_interface_copy_media_options_available(
                self.raw.as_ptr(),
                u8::from(filter),
            ))
        }
        .map(PropertyList::from_owned_handle);
        NetworkInterfaceMediaOptions {
            current,
            active,
            available,
        }
    }

    pub fn media_subtypes(available: &PropertyList) -> Vec<String> {
        bridge::take_string_array(unsafe {
            ffi::network_interface::sc_network_interface_copy_media_subtypes(available.as_ptr())
        })
    }

    pub fn media_subtype_options(available: &PropertyList, subtype: &str) -> Result<Vec<Vec<String>>> {
        let subtype = bridge::cstring(subtype, "sc_network_interface_copy_media_subtype_options")?;
        bridge::parse_json(
            "sc_network_interface_copy_media_subtype_options",
            unsafe {
                ffi::network_interface::sc_network_interface_copy_media_subtype_options(
                    available.as_ptr(),
                    subtype.as_ptr(),
                )
            },
        )
    }

    pub fn mtu_info(&self) -> Result<Option<NetworkInterfaceMtuInfo>> {
        let raw = unsafe { ffi::network_interface::sc_network_interface_copy_mtu_info(self.raw.as_ptr()) };
        if raw.is_null() {
            return Ok(None);
        }
        bridge::parse_json("sc_network_interface_copy_mtu_info", raw).map(Some)
    }

    pub fn set_configuration(&self, config: Option<&PropertyList>) -> Result<()> {
        let ok = unsafe {
            ffi::network_interface::sc_network_interface_set_configuration(
                self.raw.as_ptr(),
                config.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
            )
        };
        bridge::bool_result("sc_network_interface_set_configuration", ok)
    }

    pub fn set_extended_configuration(
        &self,
        extended_type: &str,
        config: Option<&PropertyList>,
    ) -> Result<()> {
        let extended_type = bridge::cstring(extended_type, "sc_network_interface_set_extended_configuration")?;
        let ok = unsafe {
            ffi::network_interface::sc_network_interface_set_extended_configuration(
                self.raw.as_ptr(),
                extended_type.as_ptr(),
                config.map_or(std::ptr::null_mut(), PropertyList::as_ptr),
            )
        };
        bridge::bool_result("sc_network_interface_set_extended_configuration", ok)
    }

    pub fn set_media_options<S: AsRef<str>>(&self, subtype: Option<&str>, options: &[S]) -> Result<()> {
        let subtype = bridge::optional_cstring(subtype, "sc_network_interface_set_media_options")?;
        let options = bridge::CStringArray::new(options, "sc_network_interface_set_media_options")?;
        let ok = unsafe {
            ffi::network_interface::sc_network_interface_set_media_options(
                self.raw.as_ptr(),
                subtype.as_ref().map_or(std::ptr::null(), |value| value.as_ptr()),
                options.as_ptr(),
                options.count(),
            )
        };
        bridge::bool_result("sc_network_interface_set_media_options", ok)
    }

    pub fn set_mtu(&self, mtu: i32) -> Result<()> {
        let ok = unsafe { ffi::network_interface::sc_network_interface_set_mtu(self.raw.as_ptr(), mtu) };
        bridge::bool_result("sc_network_interface_set_mtu", ok)
    }

    pub fn force_configuration_refresh(&self) -> Result<()> {
        let ok = unsafe { ffi::network_interface::sc_network_interface_force_configuration_refresh(self.raw.as_ptr()) };
        bridge::bool_result("sc_network_interface_force_configuration_refresh", ok)
    }

    pub(crate) fn from_owned_handle(raw: bridge::OwnedHandle) -> Self {
        Self { raw }
    }

    pub(crate) fn as_ptr(&self) -> bridge::RawHandle {
        self.raw.as_ptr()
    }
}