scuffle 0.1.0

High-level bindings to libscf on illumos
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::Scf;
use crate::buf::scf_get_name;
use crate::error::HandleCreateError;
use crate::error::IterError;
use crate::error::IterErrorKind;
use crate::error::LibscfError;
use crate::error::ScfEntity;
use crate::error::ToEntityDescription;
use crate::scf::ScfObject;
use crate::utf8cstring::Utf8CString;
use std::marker::PhantomData;

mod sealed {
    pub(crate) trait ScfIterable: crate::scf::ScfObjectType {
        const ENTITY: crate::error::ScfEntity;

        unsafe fn try_next(
            iter: *mut libscf_sys::scf_iter_t,
            uninitialized_item: *mut Self,
        ) -> libc::c_int;
    }

    pub(crate) trait ScfNamedIterable: ScfIterable {
        unsafe fn get_name(
            item: *const Self,
            buf: *mut libc::c_char,
            buf_len: usize,
        ) -> libc::ssize_t;
    }
}

impl sealed::ScfIterable for libscf_sys::scf_value_t {
    const ENTITY: ScfEntity = ScfEntity::Value;

    unsafe fn try_next(
        iter: *mut libscf_sys::scf_iter_t,
        uninitialized_item: *mut Self,
    ) -> libc::c_int {
        unsafe { libscf_sys::scf_iter_next_value(iter, uninitialized_item) }
    }
}

impl sealed::ScfIterable for libscf_sys::scf_propertygroup_t {
    const ENTITY: ScfEntity = ScfEntity::PropertyGroup;

    unsafe fn try_next(
        iter: *mut libscf_sys::scf_iter_t,
        uninitialized_item: *mut Self,
    ) -> libc::c_int {
        unsafe { libscf_sys::scf_iter_next_pg(iter, uninitialized_item) }
    }
}

impl sealed::ScfNamedIterable for libscf_sys::scf_propertygroup_t {
    unsafe fn get_name(
        item: *const Self,
        buf: *mut libc::c_char,
        buf_len: usize,
    ) -> libc::ssize_t {
        unsafe { libscf_sys::scf_pg_get_name(item, buf, buf_len) }
    }
}

impl sealed::ScfIterable for libscf_sys::scf_property_t {
    const ENTITY: ScfEntity = ScfEntity::Property;

    unsafe fn try_next(
        iter: *mut libscf_sys::scf_iter_t,
        uninitialized_item: *mut Self,
    ) -> libc::c_int {
        unsafe { libscf_sys::scf_iter_next_property(iter, uninitialized_item) }
    }
}

impl sealed::ScfNamedIterable for libscf_sys::scf_property_t {
    unsafe fn get_name(
        item: *const Self,
        buf: *mut libc::c_char,
        buf_len: usize,
    ) -> libc::ssize_t {
        unsafe { libscf_sys::scf_property_get_name(item, buf, buf_len) }
    }
}

impl sealed::ScfIterable for libscf_sys::scf_instance_t {
    const ENTITY: ScfEntity = ScfEntity::Instance;

    unsafe fn try_next(
        iter: *mut libscf_sys::scf_iter_t,
        uninitialized_item: *mut Self,
    ) -> libc::c_int {
        unsafe { libscf_sys::scf_iter_next_instance(iter, uninitialized_item) }
    }
}

impl sealed::ScfNamedIterable for libscf_sys::scf_instance_t {
    unsafe fn get_name(
        item: *const Self,
        buf: *mut libc::c_char,
        buf_len: usize,
    ) -> libc::ssize_t {
        unsafe { libscf_sys::scf_instance_get_name(item, buf, buf_len) }
    }
}

impl sealed::ScfIterable for libscf_sys::scf_snapshot_t {
    const ENTITY: ScfEntity = ScfEntity::Snapshot;

    unsafe fn try_next(
        iter: *mut libscf_sys::scf_iter_t,
        uninitialized_item: *mut Self,
    ) -> libc::c_int {
        unsafe { libscf_sys::scf_iter_next_snapshot(iter, uninitialized_item) }
    }
}

impl sealed::ScfNamedIterable for libscf_sys::scf_snapshot_t {
    unsafe fn get_name(
        item: *const Self,
        buf: *mut libc::c_char,
        buf_len: usize,
    ) -> libc::ssize_t {
        unsafe { libscf_sys::scf_snapshot_get_name(item, buf, buf_len) }
    }
}

pub(crate) struct ScfUninitializedIter<'a> {
    handle: ScfObject<'a, libscf_sys::scf_iter_t>,
}

impl<'a> ScfUninitializedIter<'a> {
    pub(crate) fn new(scf: &'a Scf<'a>) -> Result<Self, HandleCreateError> {
        Ok(Self { handle: scf.scf_iter_create()? })
    }

    pub(crate) unsafe fn init_property_values(
        mut self,
        property: *const libscf_sys::scf_property_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_value_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_property_values(
                self.handle.as_mut_ptr(),
                property,
            )
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_service_instances(
        mut self,
        service: *const libscf_sys::scf_service_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_instance_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_service_instances(
                self.handle.as_mut_ptr(),
                service,
            )
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_service_property_groups(
        mut self,
        service: *const libscf_sys::scf_service_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_propertygroup_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_service_pgs(self.handle.as_mut_ptr(), service)
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_instance_property_groups(
        mut self,
        instance: *const libscf_sys::scf_instance_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_propertygroup_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_instance_pgs(
                self.handle.as_mut_ptr(),
                instance,
            )
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_instance_property_groups_composed(
        mut self,
        instance: *const libscf_sys::scf_instance_t,
        snapshot: *const libscf_sys::scf_snapshot_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_propertygroup_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_instance_pgs_composed(
                self.handle.as_mut_ptr(),
                instance,
                snapshot,
            )
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_instance_snapshots(
        mut self,
        instance: *const libscf_sys::scf_instance_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_snapshot_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_instance_snapshots(
                self.handle.as_mut_ptr(),
                instance,
            )
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }

    pub(crate) unsafe fn init_property_group_properties(
        mut self,
        pg: *const libscf_sys::scf_propertygroup_t,
    ) -> Result<ScfIter<'a, libscf_sys::scf_property_t>, LibscfError> {
        LibscfError::from_ret(unsafe {
            libscf_sys::scf_iter_pg_properties(self.handle.as_mut_ptr(), pg)
        })?;
        Ok(ScfIter { handle: self.handle, _inner: PhantomData })
    }
}

pub(crate) struct ScfIter<'a, T> {
    handle: ScfObject<'a, libscf_sys::scf_iter_t>,
    _inner: PhantomData<fn() -> T>,
}

impl<'a, T: sealed::ScfIterable> ScfIter<'a, T> {
    pub(crate) fn next_with_handle<P>(
        &mut self,
        parent: &P,
        handle: &mut ScfObject<'a, T>,
    ) -> Option<Result<(), IterError>>
    where
        P: ToEntityDescription,
    {
        match unsafe {
            T::try_next(self.handle.as_mut_ptr(), handle.as_mut_ptr())
        } {
            0 => None,
            1 => Some(Ok(())),
            _ => Some(Err(IterError::Iter {
                entity: T::ENTITY,
                parent: parent.to_entity_description(),
                kind: IterErrorKind::GetNext(LibscfError::last()),
            })),
        }
    }
}

impl<'a, T: sealed::ScfNamedIterable> ScfIter<'a, T> {
    pub(crate) fn next_named<F, P>(
        &mut self,
        parent: &P,
        make_handle: F,
    ) -> Option<Result<(Utf8CString, ScfObject<'a, T>), IterError>>
    where
        P: ToEntityDescription,
        F: FnOnce() -> Result<ScfObject<'a, T>, HandleCreateError>,
    {
        let mut handle = match make_handle() {
            Ok(handle) => handle,
            Err(err) => return Some(Err(err.into())),
        };

        match self.next_with_handle(parent, &mut handle)? {
            Ok(()) => (),
            Err(err) => return Some(Err(err)),
        }

        let name = match scf_get_name(|buf, buf_len| unsafe {
            T::get_name(handle.as_ptr(), buf, buf_len)
        }) {
            Ok(name) => name,
            Err(err) => {
                return Some(Err(IterError::Iter {
                    entity: T::ENTITY,
                    parent: parent.to_entity_description(),
                    kind: IterErrorKind::GetName(err),
                }));
            }
        };

        Some(Ok((name, handle)))
    }
}