photokit 0.2.0

Safe Rust bindings for Apple's Photos framework — photo library access on macOS
Documentation
use std::ptr;

use base64::Engine;
use serde::Serialize;

use crate::error::PhotoKitError;
use crate::ffi;
use crate::private::{json_cstring, take_string};

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct PHAssetResourceCreationOptions {
    pub original_filename: Option<String>,
    pub content_type_identifier: Option<String>,
    pub uniform_type_identifier: Option<String>,
    #[serde(default)]
    pub should_move_file: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PHAssetCreationResource {
    pub resource_type: i64,
    pub file_url: Option<String>,
    pub data_base64: Option<String>,
    pub options: Option<PHAssetResourceCreationOptions>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct PHAssetCreationRequest {
    pub resources: Vec<PHAssetCreationResource>,
}

impl PHAssetCreationRequest {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn add_file_resource(
        mut self,
        resource_type: i64,
        file_url: impl Into<String>,
        options: Option<PHAssetResourceCreationOptions>,
    ) -> Self {
        self.resources.push(PHAssetCreationResource {
            resource_type,
            file_url: Some(file_url.into()),
            data_base64: None,
            options,
        });
        self
    }

    pub fn add_data_resource(
        mut self,
        resource_type: i64,
        data_base64: impl Into<String>,
        options: Option<PHAssetResourceCreationOptions>,
    ) -> Self {
        self.resources.push(PHAssetCreationResource {
            resource_type,
            file_url: None,
            data_base64: Some(data_base64.into()),
            options,
        });
        self
    }

    pub fn add_data_resource_bytes(
        self,
        resource_type: i64,
        data: &[u8],
        options: Option<PHAssetResourceCreationOptions>,
    ) -> Self {
        self.add_data_resource(
            resource_type,
            base64::engine::general_purpose::STANDARD.encode(data),
            options,
        )
    }

    pub fn is_empty(&self) -> bool {
        self.resources.is_empty()
    }

    pub fn supports_asset_resource_types(resource_types: &[i64]) -> Result<bool, PhotoKitError> {
        let resource_types_json = json_cstring(resource_types, "resource types")?;
        let mut error = ptr::null_mut();
        let supported = unsafe {
            ffi::ph_asset_creation_request_supports_resource_types(
                resource_types_json.as_ptr(),
                &mut error,
            )
        };
        if error.is_null() {
            Ok(supported != 0)
        } else {
            Err(unsafe {
                PhotoKitError::from_error_ptr(
                    error,
                    "asset creation resource type support lookup failed",
                )
            })
        }
    }

    pub fn perform(self) -> Result<String, PhotoKitError> {
        let resources_json = json_cstring(&self.resources, "asset creation resources")?;
        let mut error = ptr::null_mut();
        let payload = unsafe { ffi::ph_asset_creation_request_perform(resources_json.as_ptr(), &mut error) };
        if payload.is_null() {
            Err(unsafe { PhotoKitError::from_error_ptr(error, "asset creation failed") })
        } else {
            unsafe { take_string(payload) }.ok_or_else(|| {
                PhotoKitError::OperationFailed("missing asset creation payload".to_owned())
            })
        }
    }
}