osakit 0.3.1

OSAKit macOS Framework adapted for Rust
Documentation
use crate::Value;
use objc2::{rc::Retained, AllocAnyThread};
use objc2_foundation::{NSArray, NSDictionary, NSNull, NSNumber, NSObject, NSString};
use std::ops::Deref;
use thiserror::Error;

#[derive(Error, Debug, PartialEq, Eq)]
pub enum ScriptInputConversionError {
    #[error("number conversion error: `{0}`")]
    NumberConversionError(String),
}

fn value_to_nsobject(value: Value) -> Result<Retained<NSObject>, ScriptInputConversionError> {
    Ok(unsafe {
        match value {
            Value::String(s) => Retained::cast_unchecked(NSString::from_str(&s)),
            Value::Bool(b) => {
                Retained::cast_unchecked(NSNumber::initWithBool(NSNumber::alloc(), b))
            }
            Value::Number(n) => Retained::cast_unchecked(if n.is_f64() {
                n.as_f64()
                    .map(|f| NSNumber::initWithDouble(NSNumber::alloc(), f))
                    .ok_or_else(|| {
                        ScriptInputConversionError::NumberConversionError(n.to_string())
                    })?
            } else if n.is_i64() {
                n.as_i64()
                    .map(|l| NSNumber::initWithLongLong(NSNumber::alloc(), l))
                    .ok_or_else(|| {
                        ScriptInputConversionError::NumberConversionError(n.to_string())
                    })?
            } else {
                n.as_u64()
                    .map(|l| NSNumber::initWithUnsignedLongLong(NSNumber::alloc(), l))
                    .ok_or_else(|| {
                        ScriptInputConversionError::NumberConversionError(n.to_string())
                    })?
            }),
            Value::Null => Retained::cast_unchecked(NSNull::null()),
            Value::Array(vec) => Retained::cast_unchecked(values_vec_to_ns_array(vec)?),
            Value::Object(obj) => {
                let mut keys: Vec<Retained<NSString>> = Vec::new();
                let mut values: Vec<Retained<NSObject>> = Vec::new();
                for (key, value) in obj.into_iter() {
                    keys.push(NSString::from_str(&key));
                    values.push(value_to_nsobject(value)?)
                }
                let key_refs: Vec<&NSString> = keys.iter().map(|k| k.deref()).collect();
                Retained::cast_unchecked(NSDictionary::from_retained_objects(&key_refs, &values))
            }
        }
    })
}

pub(crate) fn values_vec_to_ns_array<I: IntoIterator<Item = Value>>(
    values: I,
) -> Result<Retained<NSArray>, ScriptInputConversionError> {
    let mut vec: Vec<Retained<NSObject>> = Vec::new();

    for item in values {
        vec.push(value_to_nsobject(item)?);
    }

    Ok(unsafe { Retained::cast_unchecked::<NSArray>(NSArray::from_retained_slice(&vec)) })
}