apple-cf 0.11.7

Safe Rust bindings for Apple's shared Core* frameworks (CoreFoundation, CoreMedia, CoreVideo, CoreGraphics, IOSurface, Dispatch).
Documentation
use std::ptr;

use super::CFURL;
use crate::raw;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i64)]
pub enum CFURLComponent {
    Scheme = 1,
    Authority = 2,
    Path = 3,
    ResourceSpecifier = 4,
    User = 5,
    Password = 6,
    Host = 8,
    Port = 9,
    Parameters = 10,
    Query = 11,
    Fragment = 12,
}

impl CFURL {
    #[must_use]
    pub fn parse(value: &str, base: Option<&Self>) -> Option<Self> {
        let length = i64::try_from(value.len()).ok()?;
        let reference = unsafe {
            raw::CFURLCreateWithBytes(
                ptr::null(),
                value.as_ptr(),
                length,
                0x0800_0100,
                base.map_or(ptr::null(), |base| base.as_ptr().cast()),
            )
        };
        let reference = unsafe { Self::from_raw(reference.cast_mut().cast()) }?;
        if base.is_none() {
            return Some(reference);
        }
        let absolute = unsafe { raw::CFURLCopyAbsoluteURL(reference.as_ptr().cast()) };
        unsafe { Self::from_raw(absolute.cast_mut().cast()) }
    }

    #[must_use]
    pub fn component(&self, component: CFURLComponent) -> Option<String> {
        let url = self.as_ptr().cast();
        let range =
            unsafe { raw::CFURLGetByteRangeForComponent(url, component as i64, ptr::null_mut()) };
        let start = usize::try_from(range.location).ok()?;
        let length = usize::try_from(range.length).ok()?;
        let byte_count = unsafe { raw::CFURLGetBytes(url, ptr::null_mut(), 0) };
        let mut bytes = vec![0; usize::try_from(byte_count).ok()?];
        let copied = unsafe { raw::CFURLGetBytes(url, bytes.as_mut_ptr(), byte_count) };
        if copied != byte_count {
            return None;
        }
        let end = start.checked_add(length)?;
        String::from_utf8(bytes.get(start..end)?.to_vec()).ok()
    }
}