libshumate 0.8.0

Rust bindings for libshumate
// Take a look at the license at the top of the repository in the LICENSE file.

// rustdoc-stripper-ignore-next
//! Traits intended for subclassing [`DataSource`](crate::DataSource).

#[cfg(feature = "v1_1")]
#[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
use crate::DataSourceRequest;
use crate::{DataSource, prelude::*};
use gio::Cancellable;
use glib::subclass::prelude::*;
use glib::translate::*;
use std::{future::Future, pin::Pin};

pub trait DataSourceImpl: ObjectImpl {
    fn tile_data_future(
        &self,
        x: i32,
        y: i32,
        zoom_level: i32,
    ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>>;

    #[cfg(feature = "v1_1")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
    fn start_request(
        &self,
        x: i32,
        y: i32,
        zoom_level: i32,
        cancellable: Option<gio::Cancellable>,
    ) -> Option<DataSourceRequest> {
        self.parent_start_request(x, y, zoom_level, cancellable)
    }
}

pub trait DataSourceImplExt: ObjectSubclass {
    #[allow(clippy::type_complexity)]
    fn parent_tile_data_async<
        Q: IsA<Cancellable>,
        C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
    >(
        &self,
        x: i32,
        y: i32,
        zoom_level: i32,
        cancellable: Option<&Q>,
        callback: C,
    ) {
        unsafe {
            let data = Self::type_data();
            let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
            let f = (*parent_class)
                .get_tile_data_async
                .expect("no parent \"get_tile_data_async\" implementation");
            let finish = (*parent_class)
                .get_tile_data_finish
                .expect("no parent \"get_tile_data_finish\" implementation");

            let user_data: Box<(C, _)> = Box::new((callback, finish));

            unsafe extern "C" fn parent_get_tile_data_async_trampoline<
                C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
            >(
                source_object_ptr: *mut glib::gobject_ffi::GObject,
                res: *mut gio::ffi::GAsyncResult,
                user_data: glib::ffi::gpointer,
            ) {
                unsafe {
                    let mut error = std::ptr::null_mut();
                    let cb: Box<(
                        C,
                        fn(
                            *mut ffi::ShumateDataSource,
                            *mut gio::ffi::GAsyncResult,
                            *mut *mut glib::ffi::GError,
                        ) -> *mut glib::ffi::GBytes,
                    )> = Box::from_raw(user_data as *mut _);
                    let bytes = cb.1(source_object_ptr as _, res, &mut error);
                    let result = if error.is_null() {
                        Ok(from_glib_full(bytes))
                    } else {
                        Err(from_glib_full(error))
                    };
                    cb.0(result);
                }
            }

            let cancellable = cancellable.map(|p| p.as_ref());
            let callback = parent_get_tile_data_async_trampoline::<C>;
            f(
                self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
                x,
                y,
                zoom_level,
                cancellable.to_glib_none().0,
                Some(callback),
                Box::into_raw(user_data) as *mut _,
            );
        }
    }

    fn parent_tile_data_future(
        &self,
        x: i32,
        y: i32,
        zoom_level: i32,
    ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>> {
        Box::pin(gio::GioFuture::new(
            &self.ref_counted(),
            move |imp, cancellable, send| {
                imp.parent_tile_data_async(x, y, zoom_level, Some(cancellable), move |res| {
                    send.resolve(res);
                });
            },
        ))
    }

    #[cfg(feature = "v1_1")]
    #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
    fn parent_start_request(
        &self,
        x: i32,
        y: i32,
        zoom_level: i32,
        cancellable: Option<gio::Cancellable>,
    ) -> Option<DataSourceRequest> {
        unsafe {
            let data = Self::type_data();
            let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
            if let Some(f) = (*parent_class).start_request {
                from_glib_full(f(
                    self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
                    x,
                    y,
                    zoom_level,
                    cancellable.to_glib_none().0,
                ))
            } else {
                None
            }
        }
    }
}

impl<T: DataSourceImpl> DataSourceImplExt for T {}

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

        let klass = class.as_mut();
        klass.get_tile_data_async = Some(data_source_get_tile_data_async::<T>);
        klass.get_tile_data_finish = Some(data_source_get_tile_data_finish);
        #[cfg(feature = "v1_1")]
        {
            klass.start_request = Some(data_source_start_request::<T>);
        }
    }
}

unsafe extern "C" fn data_source_get_tile_data_async<T: DataSourceImpl>(
    ptr: *mut ffi::ShumateDataSource,
    x: i32,
    y: i32,
    zoom_level: i32,
    cancellable_ptr: *mut gio::ffi::GCancellable,
    callback: gio::ffi::GAsyncReadyCallback,
    user_data: glib::ffi::gpointer,
) {
    unsafe {
        let instance = &*(ptr as *mut T::Instance);
        let imp = instance.imp();
        let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);

        let closure = move |result: gio::LocalTask<glib::Bytes>,
                            source_object: Option<&DataSource>| {
            let result: *mut gio::ffi::GAsyncResult = result
                .unsafe_cast_ref::<gio::AsyncResult>()
                .to_glib_none()
                .0;
            let source_object = source_object
                .map(|o| o.unsafe_cast_ref::<glib::Object>())
                .to_glib_none()
                .0;
            callback.unwrap()(source_object, result, user_data)
        };

        let t = gio::LocalTask::new(None, cancellable.as_ref(), closure);

        glib::MainContext::default().spawn_local(async move {
            let res = imp.tile_data_future(x, y, zoom_level).await;
            t.return_result(res);
        });
    }
}

unsafe extern "C" fn data_source_get_tile_data_finish(
    _ptr: *mut ffi::ShumateDataSource,
    res_ptr: *mut gio::ffi::GAsyncResult,
    error_ptr: *mut *mut glib::ffi::GError,
) -> *mut glib::ffi::GBytes {
    unsafe {
        let res: gio::AsyncResult = from_glib_none(res_ptr);
        let task = res.downcast::<gio::LocalTask<glib::Bytes>>().unwrap();
        match task.propagate() {
            Ok(bytes) => bytes.to_glib_full(),
            Err(e) => {
                *error_ptr = e.into_glib_ptr();
                std::ptr::null_mut()
            }
        }
    }
}

#[cfg(feature = "v1_1")]
unsafe extern "C" fn data_source_start_request<T: DataSourceImpl>(
    ptr: *mut ffi::ShumateDataSource,
    x: i32,
    y: i32,
    zoom_level: i32,
    cancellable_ptr: *mut gio::ffi::GCancellable,
) -> *mut ffi::ShumateDataSourceRequest {
    unsafe {
        let instance = &*(ptr as *mut T::Instance);
        let imp = instance.imp();

        imp.start_request(x, y, zoom_level, from_glib_none(cancellable_ptr))
            .to_glib_full()
    }
}