Skip to main content

libshumate/subclass/
data_source.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3// rustdoc-stripper-ignore-next
4//! Traits intended for subclassing [`DataSource`](crate::DataSource).
5
6#[cfg(feature = "v1_1")]
7#[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
8use crate::DataSourceRequest;
9use crate::{DataSource, prelude::*};
10use gio::Cancellable;
11use glib::subclass::prelude::*;
12use glib::translate::*;
13use std::{future::Future, pin::Pin};
14
15pub trait DataSourceImpl: ObjectImpl {
16    fn tile_data_future(
17        &self,
18        x: i32,
19        y: i32,
20        zoom_level: i32,
21    ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>>;
22
23    /// Begins a request for a tile.
24    /// ## `x`
25    /// X coordinate to request
26    /// ## `y`
27    /// Y coordinate to request
28    /// ## `zoom_level`
29    /// zoom level to request
30    /// ## `cancellable`
31    /// for cancelling the request
32    ///
33    /// # Returns
34    ///
35    /// a [`DataSourceRequest`][crate::DataSourceRequest] object for tracking
36    /// the request.
37    #[cfg(feature = "v1_1")]
38    #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
39    fn start_request(
40        &self,
41        x: i32,
42        y: i32,
43        zoom_level: i32,
44        cancellable: Option<gio::Cancellable>,
45    ) -> Option<DataSourceRequest> {
46        self.parent_start_request(x, y, zoom_level, cancellable)
47    }
48}
49
50pub trait DataSourceImplExt: ObjectSubclass {
51    #[allow(clippy::type_complexity)]
52    fn parent_tile_data_async<
53        Q: IsA<Cancellable>,
54        C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
55    >(
56        &self,
57        x: i32,
58        y: i32,
59        zoom_level: i32,
60        cancellable: Option<&Q>,
61        callback: C,
62    ) {
63        unsafe {
64            let data = Self::type_data();
65            let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
66            let f = (*parent_class)
67                .get_tile_data_async
68                .expect("no parent \"get_tile_data_async\" implementation");
69            let finish = (*parent_class)
70                .get_tile_data_finish
71                .expect("no parent \"get_tile_data_finish\" implementation");
72
73            let user_data: Box<(C, _)> = Box::new((callback, finish));
74
75            unsafe extern "C" fn parent_get_tile_data_async_trampoline<
76                C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
77            >(
78                source_object_ptr: *mut glib::gobject_ffi::GObject,
79                res: *mut gio::ffi::GAsyncResult,
80                user_data: glib::ffi::gpointer,
81            ) {
82                unsafe {
83                    let mut error = std::ptr::null_mut();
84                    let cb: Box<(
85                        C,
86                        fn(
87                            *mut ffi::ShumateDataSource,
88                            *mut gio::ffi::GAsyncResult,
89                            *mut *mut glib::ffi::GError,
90                        ) -> *mut glib::ffi::GBytes,
91                    )> = Box::from_raw(user_data as *mut _);
92                    let bytes = cb.1(source_object_ptr as _, res, &mut error);
93                    let result = if error.is_null() {
94                        Ok(from_glib_full(bytes))
95                    } else {
96                        Err(from_glib_full(error))
97                    };
98                    cb.0(result);
99                }
100            }
101
102            let cancellable = cancellable.map(|p| p.as_ref());
103            let callback = parent_get_tile_data_async_trampoline::<C>;
104            f(
105                self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
106                x,
107                y,
108                zoom_level,
109                cancellable.to_glib_none().0,
110                Some(callback),
111                Box::into_raw(user_data) as *mut _,
112            );
113        }
114    }
115
116    fn parent_tile_data_future(
117        &self,
118        x: i32,
119        y: i32,
120        zoom_level: i32,
121    ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>> {
122        Box::pin(gio::GioFuture::new(
123            &self.ref_counted(),
124            move |imp, cancellable, send| {
125                imp.parent_tile_data_async(x, y, zoom_level, Some(cancellable), move |res| {
126                    send.resolve(res);
127                });
128            },
129        ))
130    }
131
132    #[cfg(feature = "v1_1")]
133    #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
134    fn parent_start_request(
135        &self,
136        x: i32,
137        y: i32,
138        zoom_level: i32,
139        cancellable: Option<gio::Cancellable>,
140    ) -> Option<DataSourceRequest> {
141        unsafe {
142            let data = Self::type_data();
143            let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
144            if let Some(f) = (*parent_class).start_request {
145                from_glib_full(f(
146                    self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
147                    x,
148                    y,
149                    zoom_level,
150                    cancellable.to_glib_none().0,
151                ))
152            } else {
153                None
154            }
155        }
156    }
157}
158
159impl<T: DataSourceImpl> DataSourceImplExt for T {}
160
161unsafe impl<T: DataSourceImpl> IsSubclassable<T> for DataSource {
162    fn class_init(class: &mut glib::Class<Self>) {
163        Self::parent_class_init::<T>(class);
164
165        let klass = class.as_mut();
166        klass.get_tile_data_async = Some(data_source_get_tile_data_async::<T>);
167        klass.get_tile_data_finish = Some(data_source_get_tile_data_finish);
168        #[cfg(feature = "v1_1")]
169        {
170            klass.start_request = Some(data_source_start_request::<T>);
171        }
172    }
173}
174
175unsafe extern "C" fn data_source_get_tile_data_async<T: DataSourceImpl>(
176    ptr: *mut ffi::ShumateDataSource,
177    x: i32,
178    y: i32,
179    zoom_level: i32,
180    cancellable_ptr: *mut gio::ffi::GCancellable,
181    callback: gio::ffi::GAsyncReadyCallback,
182    user_data: glib::ffi::gpointer,
183) {
184    unsafe {
185        let instance = &*(ptr as *mut T::Instance);
186        let imp = instance.imp();
187        let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);
188
189        let closure = move |result: gio::LocalTask<glib::Bytes>,
190                            source_object: Option<&DataSource>| {
191            let result: *mut gio::ffi::GAsyncResult = result
192                .unsafe_cast_ref::<gio::AsyncResult>()
193                .to_glib_none()
194                .0;
195            let source_object = source_object
196                .map(|o| o.unsafe_cast_ref::<glib::Object>())
197                .to_glib_none()
198                .0;
199            callback.unwrap()(source_object, result, user_data)
200        };
201
202        let t = gio::LocalTask::new(None, cancellable.as_ref(), closure);
203
204        glib::MainContext::default().spawn_local(async move {
205            let res = imp.tile_data_future(x, y, zoom_level).await;
206            t.return_result(res);
207        });
208    }
209}
210
211unsafe extern "C" fn data_source_get_tile_data_finish(
212    _ptr: *mut ffi::ShumateDataSource,
213    res_ptr: *mut gio::ffi::GAsyncResult,
214    error_ptr: *mut *mut glib::ffi::GError,
215) -> *mut glib::ffi::GBytes {
216    unsafe {
217        let res: gio::AsyncResult = from_glib_none(res_ptr);
218        let task = res.downcast::<gio::LocalTask<glib::Bytes>>().unwrap();
219        match task.propagate() {
220            Ok(bytes) => bytes.to_glib_full(),
221            Err(e) => {
222                *error_ptr = e.into_glib_ptr();
223                std::ptr::null_mut()
224            }
225        }
226    }
227}
228
229#[cfg(feature = "v1_1")]
230unsafe extern "C" fn data_source_start_request<T: DataSourceImpl>(
231    ptr: *mut ffi::ShumateDataSource,
232    x: i32,
233    y: i32,
234    zoom_level: i32,
235    cancellable_ptr: *mut gio::ffi::GCancellable,
236) -> *mut ffi::ShumateDataSourceRequest {
237    unsafe {
238        let instance = &*(ptr as *mut T::Instance);
239        let imp = instance.imp();
240
241        imp.start_request(x, y, zoom_level, from_glib_none(cancellable_ptr))
242            .to_glib_full()
243    }
244}