ui-automation 0.1.1

Control the User Interface, cross-platform.
Documentation
// Copyright (C) 2024 Tristan Gerritsen <tristan@thewoosh.org>
// All Rights Reserved.

use std::{ffi::{c_void, CString}, fmt::Display, ptr::null};

use crate::{UIErrorKind, UIResult};

use super::{CFAllocatorRef, CFData, CFDataRef, CFIndex, CFRange, CFSerializable, CFType};

pub type CFStringRef = *const c_void;

#[derive(Debug, Clone)]
pub struct CFString {
    inner: CFType,
}

impl CFString {
    pub fn new(s: &str) -> Option<Self> {
        let s = CString::new(s).unwrap();

        let alloc = null();
        let c_str = s.as_ptr() as *const u8;
        let encoding = CFStringEncoding::Utf8;

        let reference = unsafe { CFStringCreateWithCString(alloc, c_str, encoding) };

        let Some(inner) = CFType::from_create(reference) else {
            return None;
        };

        Some(Self { inner })
    }

    #[must_use]
    pub fn from_get(str_ref: CFType) -> Option<Self> {
        Some(Self {
            inner: str_ref,
        })
    }

    #[must_use]
    pub fn from_create(str_ref: CFStringRef) -> Option<Self> {
        Some(Self {
            inner: CFType::from_create(str_ref)?,
        })
    }

    pub fn to_string(&self) -> UIResult<String> {
        let alloc = null();
        let data_ref = unsafe {
            CFStringCreateExternalRepresentation(alloc, self.inner.ptr(), CFStringEncoding::Utf8, b'?')
        };

        let Some(data) = CFData::from_create(data_ref) else {
            return Err(UIErrorKind::AllocationFailed { resource: "CFStringCreateExternalRepresentation" }.into());
        };

        Ok(String::from_utf8_lossy(data.as_slice()).to_string())
    }
}

impl Display for CFString {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_string().unwrap().fmt(f)
    }
}

impl From<&'static str> for CFString {
    fn from(value: &'static str) -> Self {
        Self::new(value).unwrap()
    }
}

impl CFSerializable for CFString {
    fn from_ptr(ptr: *const c_void) -> Self {
        Self {
            inner: CFType::from_get(ptr).unwrap(),
        }
    }

    fn to_ptr(&self) -> *const c_void {
        self.inner.ptr()
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum CFStringEncoding {
    Utf8 = 0x08000100,
}

#[allow(unused)]
extern "C-unwind" {
    pub(super) fn CFStringCreateWithCString(alloc: CFAllocatorRef, c_str: *const u8, encoding: CFStringEncoding) -> CFStringRef;
    pub(super) fn CFStringGetBytes(the_string: CFStringRef, range: CFRange, encoding: CFStringEncoding, loss_byte: u8, is_external_representation: bool, buffer: *mut u8, max_buf_len: CFIndex, used_buf_len: *mut CFIndex) -> CFIndex;
    pub(super) fn CFStringCreateExternalRepresentation(alloc: CFAllocatorRef, the_string: CFStringRef, encoding: CFStringEncoding, loss_byte: u8) -> CFDataRef;
}