clepsydre-rebind 0.1.0

Safe bindings for libclepsydre
// rustdoc-stripper-ignore-next
//! Traits intended for subclassing [`Event`].

use std::{ffi::c_char, ptr};

use ffi::{ClepsydreCalendar, ClepsydreTimeframe};
use gio::ffi::{
    GAsyncReadyCallback, GAsyncResult, GCancellable, g_task_new, g_task_propagate_boolean,
    g_task_return_boolean, g_task_return_error,
};
use glib::{
    ffi::{GError, GFALSE, GTRUE, gboolean, gpointer},
    gobject_ffi::g_object_unref,
    prelude::*,
    subclass::prelude::*,
    translate::*,
};

use crate::{
    Calendar, Event, PinnedFutureResult, Timeframe,
    ffi::{ClepsydreEvent, ClepsydreEventClass},
};

pub trait EventImpl: ObjectImpl + ObjectSubclass<Type: IsA<Event>> {
    fn try_update(
        &self,
        calendar: Option<&impl IsA<Calendar>>,
        name: Option<&str>,
        description: Option<&str>,
        location: Option<&str>,
        conference: Option<&str>,
        timeframe: Option<&Timeframe>,
    ) -> Result<(), glib::Error> {
        self.parent_try_update(calendar, name, description, location, conference, timeframe)
    }

    fn try_update_future(
        &self,
        calendar: Option<&impl IsA<Calendar>>,
        name: Option<&str>,
        description: Option<&str>,
        location: Option<&str>,
        conference: Option<&str>,
        timeframe: Option<&Timeframe>,
    ) -> PinnedFutureResult<()> {
        self.parent_try_update_future(calendar, name, description, location, conference, timeframe)
    }

    fn try_remove(&self) -> Result<(), glib::Error> {
        self.parent_try_remove()
    }

    fn try_remove_future(&self) -> PinnedFutureResult<()> {
        self.parent_try_remove_future()
    }
}

pub trait EventImplExt: EventImpl {
    fn parent_try_update(
        &self,
        calendar: Option<&impl IsA<Calendar>>,
        name: Option<&str>,
        description: Option<&str>,
        location: Option<&str>,
        conference: Option<&str>,
        timeframe: Option<&Timeframe>,
    ) -> Result<(), glib::Error> {
        unsafe {
            let data = Self::type_data();
            let parent_class = data.as_ref().parent_class() as *mut ClepsydreEventClass;
            let mut error = ptr::null_mut();
            if let Some(f) = (*parent_class).try_update {
                if f(
                    self.obj().unsafe_cast_ref::<Event>().to_glib_none().0,
                    if let Some(calendar) = calendar {
                        calendar.unsafe_cast_ref::<Calendar>().to_glib_none().0
                    } else {
                        ptr::null_mut()
                    },
                    name.to_glib_none().0,
                    description.to_glib_none().0,
                    location.to_glib_none().0,
                    conference.to_glib_none().0,
                    timeframe.to_glib_none().0,
                    &mut error,
                ) == GTRUE
                {
                    Ok(())
                } else {
                    Err(from_glib_full(error))
                }
            } else {
                Err(glib::Error::new(
                    crate::Error::NotImplemented,
                    "The method Event::try_remove is not implemented",
                ))
            }
        }
    }

    fn parent_try_update_future(
        &self,
        _calendar: Option<&impl IsA<Calendar>>,
        _name: Option<&str>,
        _description: Option<&str>,
        _location: Option<&str>,
        _conference: Option<&str>,
        _timeframe: Option<&Timeframe>,
    ) -> PinnedFutureResult<()> {
        unimplemented!()
    }

    fn parent_try_remove(&self) -> Result<(), glib::Error> {
        unsafe {
            let data = Self::type_data();
            let parent_class = data.as_ref().parent_class() as *mut ClepsydreEventClass;
            let mut error = ptr::null_mut();
            if let Some(f) = (*parent_class).try_remove {
                if f(
                    self.obj().unsafe_cast_ref::<Event>().to_glib_none().0,
                    &mut error,
                ) == GTRUE
                {
                    Ok(())
                } else {
                    Err(from_glib_full(error))
                }
            } else {
                Err(glib::Error::new(
                    crate::Error::NotImplemented,
                    "The method Event::try_remove is not implemented",
                ))
            }
        }
    }

    fn parent_try_remove_future(&self) -> PinnedFutureResult<()> {
        unimplemented!()
    }
}

impl<T: EventImpl> EventImplExt for T {}

unsafe impl<T: EventImpl> IsSubclassable<T> for Event {
    fn class_init(class: &mut ::glib::Class<Self>) {
        Self::parent_class_init::<T>(class);

        let klass = class.as_mut();
        klass.try_update = Some(event_try_update::<T>);
        klass.try_update_async = Some(event_try_update_async::<T>);
        klass.try_update_finish = Some(event_try_update_finish);
        klass.try_remove = Some(event_try_remove::<T>);
        klass.try_remove_async = Some(event_try_remove_async::<T>);
        klass.try_remove_finish = Some(event_try_remove_finish);
    }
}

unsafe extern "C" fn event_try_update<T: EventImpl>(
    event_ptr: *mut ClepsydreEvent,
    calendar_ptr: *mut ClepsydreCalendar,
    name_ptr: *const c_char,
    description_ptr: *const c_char,
    location_ptr: *const c_char,
    conference_ptr: *const c_char,
    timeframe_ptr: *mut ClepsydreTimeframe,
    error_ptr: *mut *mut GError,
) -> gboolean {
    unsafe {
        let instance = &*(event_ptr as *mut T::Instance);
        let imp = instance.imp();
        let calendar = if !calendar_ptr.is_null() {
            Some(&*Calendar::from_glib_borrow(calendar_ptr))
        } else {
            None
        };
        let name = if !name_ptr.is_null() {
            Some(glib::GStr::from_ptr(name_ptr).to_string().leak() as &str)
        } else {
            None
        };
        let description = if !description_ptr.is_null() {
            Some(glib::GStr::from_ptr(description_ptr).to_string().leak() as &str)
        } else {
            None
        };
        let location = if !location_ptr.is_null() {
            Some(glib::GStr::from_ptr(location_ptr).to_string().leak() as &str)
        } else {
            None
        };
        let conference = if !conference_ptr.is_null() {
            Some(glib::GStr::from_ptr(conference_ptr).to_string().leak() as &str)
        } else {
            None
        };
        let timeframe = if !timeframe_ptr.is_null() {
            Some(&*Timeframe::from_glib_borrow(timeframe_ptr))
        } else {
            None
        };

        match imp.try_update(calendar, name, description, location, conference, timeframe) {
            Ok(()) => GTRUE,
            Err(error) => {
                if !error_ptr.is_null() {
                    *error_ptr = error.to_glib_full();
                }
                GFALSE
            }
        }
    }
}

unsafe extern "C" fn event_try_update_async<T: EventImpl>(
    event_ptr: *mut ClepsydreEvent,
    calendar_ptr: *mut ClepsydreCalendar,
    name_ptr: *const c_char,
    description_ptr: *const c_char,
    location_ptr: *const c_char,
    conference_ptr: *const c_char,
    timeframe_ptr: *mut ClepsydreTimeframe,
    cancellable: *mut GCancellable,
    callback: GAsyncReadyCallback,
    user_data: gpointer,
) {
    unsafe {
        let task = g_task_new(event_ptr as *mut _, cancellable, callback, user_data);

        let instance = &*(event_ptr as *mut T::Instance);
        let imp = instance.imp();
        // TODO: Does calendar live long enough?
        let calendar = if !calendar_ptr.is_null() {
            Some(&*Calendar::from_glib_borrow(calendar_ptr))
        } else {
            None
        };
        let name = if !name_ptr.is_null() {
            Some(glib::GStr::from_ptr(name_ptr).as_str())
        } else {
            None
        };
        let description = if !description_ptr.is_null() {
            Some(glib::GStr::from_ptr(description_ptr).as_str())
        } else {
            None
        };
        let location = if !location_ptr.is_null() {
            Some(glib::GStr::from_ptr(location_ptr).as_str())
        } else {
            None
        };
        let conference = if !conference_ptr.is_null() {
            Some(glib::GStr::from_ptr(conference_ptr).as_str())
        } else {
            None
        };
        let timeframe = if !timeframe_ptr.is_null() {
            Some(&*Timeframe::from_glib_borrow(timeframe_ptr))
        } else {
            None
        };

        let future =
            imp.try_update_future(calendar, name, description, location, conference, timeframe);

        glib::MainContext::ref_thread_default().spawn_local(async move {
            match future.await {
                Ok(()) => g_task_return_boolean(task, GTRUE),
                Err(error) => g_task_return_error(task, error.into_glib_ptr()),
            }
            g_object_unref(task as *mut _);
        });
    }
}

unsafe extern "C" fn event_try_update_finish(
    _event_ptr: *mut ClepsydreEvent,
    async_result: *mut GAsyncResult,
    error_ptr: *mut *mut GError,
) -> gboolean {
    unsafe { g_task_propagate_boolean(async_result as *mut _, error_ptr) }
}

unsafe extern "C" fn event_try_remove<T: EventImpl>(
    event_ptr: *mut ClepsydreEvent,
    error_ptr: *mut *mut GError,
) -> gboolean {
    let instance = unsafe { &*(event_ptr as *mut T::Instance) };
    let imp = instance.imp();

    match imp.try_remove() {
        Ok(()) => GTRUE,
        Err(error) => {
            if !error_ptr.is_null() {
                unsafe { *error_ptr = error.to_glib_full() };
            }
            GFALSE
        }
    }
}

unsafe extern "C" fn event_try_remove_async<T: EventImpl>(
    event_ptr: *mut ClepsydreEvent,
    cancellable: *mut GCancellable,
    callback: GAsyncReadyCallback,
    user_data: gpointer,
) {
    unsafe {
        let task = g_task_new(event_ptr as *mut _, cancellable, callback, user_data);

        let instance = &*(event_ptr as *mut T::Instance);
        let imp = instance.imp();

        let future = imp.try_remove_future();

        glib::MainContext::ref_thread_default().spawn_local(async move {
            match future.await {
                Ok(()) => g_task_return_boolean(task, GTRUE),
                Err(error) => g_task_return_error(task, error.into_glib_ptr()),
            }
            g_object_unref(task as *mut _);
        });
    }
}

unsafe extern "C" fn event_try_remove_finish(
    _event_ptr: *mut ClepsydreEvent,
    async_result: *mut GAsyncResult,
    error_ptr: *mut *mut GError,
) -> gboolean {
    unsafe { g_task_propagate_boolean(async_result as *mut _, error_ptr) }
}

#[allow(clippy::missing_safety_doc)]
pub unsafe trait EventClassExt: ClassStruct {}

unsafe impl<T: ClassStruct> EventClassExt for T where T::Type: EventImpl {}