gstreamer_editing_services/subclass/
formatter.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::{ffi, prelude::*, Formatter};
4use glib::{subclass::prelude::*, translate::*};
5
6pub trait FormatterImpl: ObjectImpl + ObjectSubclass<Type: IsA<Formatter>> + Send + Sync {
7    /// Whether the URI can be loaded
8    fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
9        self.parent_can_load_uri(uri)
10    }
11
12    /// Load data from the given URI into timeline.
13    ///
14    /// # Deprecated since 1.18
15    ///
16    /// Use [`TimelineExt::load_from_uri()`][crate::prelude::TimelineExt::load_from_uri()]
17    /// ## `timeline`
18    /// a [`Timeline`][crate::Timeline]
19    /// ## `uri`
20    /// a `gchar` * pointing to a URI
21    ///
22    /// # Returns
23    ///
24    /// TRUE if the timeline data was successfully loaded from the URI,
25    /// else FALSE.
26    fn load_from_uri(&self, timeline: &crate::Timeline, uri: &str) -> Result<(), glib::Error> {
27        self.parent_load_from_uri(timeline, uri)
28    }
29
30    /// Save data from timeline to the given URI.
31    ///
32    /// # Deprecated since 1.18
33    ///
34    /// Use [`TimelineExt::save_to_uri()`][crate::prelude::TimelineExt::save_to_uri()]
35    /// ## `timeline`
36    /// a [`Timeline`][crate::Timeline]
37    /// ## `uri`
38    /// a `gchar` * pointing to a URI
39    /// ## `overwrite`
40    /// [`true`] to overwrite file if it exists
41    ///
42    /// # Returns
43    ///
44    /// TRUE if the timeline data was successfully saved to the URI
45    /// else FALSE.
46    fn save_to_uri(
47        &self,
48        timeline: &crate::Timeline,
49        uri: &str,
50        overwrite: bool,
51    ) -> Result<(), glib::Error> {
52        self.parent_save_to_uri(timeline, uri, overwrite)
53    }
54}
55
56pub trait FormatterImplExt: FormatterImpl {
57    fn parent_can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
58        unsafe {
59            let data = Self::type_data();
60            let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
61
62            let f = (*parent_class)
63                .can_load_uri
64                .expect("Missing parent function `can_load_uri`");
65
66            let mut error = std::ptr::null_mut();
67            let res = f(
68                self.obj()
69                    .unsafe_cast_ref::<crate::Formatter>()
70                    .to_glib_none()
71                    .0,
72                uri.to_glib_none().0,
73                &mut error,
74            );
75
76            if res == glib::ffi::GFALSE {
77                if error.is_null() {
78                    Err(glib::Error::new(
79                        gst::CoreError::Failed,
80                        "Can load uri failed",
81                    ))
82                } else {
83                    Err(from_glib_full(error))
84                }
85            } else {
86                Ok(())
87            }
88        }
89    }
90
91    fn parent_load_from_uri(
92        &self,
93        timeline: &crate::Timeline,
94        uri: &str,
95    ) -> Result<(), glib::Error> {
96        unsafe {
97            let data = Self::type_data();
98            let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
99
100            let f = (*parent_class)
101                .load_from_uri
102                .expect("Missing parent function `load_from_uri`");
103
104            let mut error = std::ptr::null_mut();
105            let res = f(
106                self.obj()
107                    .unsafe_cast_ref::<crate::Formatter>()
108                    .to_glib_none()
109                    .0,
110                timeline
111                    .unsafe_cast_ref::<crate::Timeline>()
112                    .to_glib_none()
113                    .0,
114                uri.to_glib_none().0,
115                &mut error,
116            );
117
118            if res == glib::ffi::GFALSE {
119                if error.is_null() {
120                    Err(glib::Error::new(
121                        gst::CoreError::Failed,
122                        "Load from uri failed",
123                    ))
124                } else {
125                    Err(from_glib_full(error))
126                }
127            } else {
128                Ok(())
129            }
130        }
131    }
132    fn parent_save_to_uri(
133        &self,
134        timeline: &crate::Timeline,
135        uri: &str,
136        overwrite: bool,
137    ) -> Result<(), glib::Error> {
138        unsafe {
139            let data = Self::type_data();
140            let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
141
142            let f = (*parent_class)
143                .save_to_uri
144                .expect("Missing parent function `save_to_uri`");
145
146            let mut error = std::ptr::null_mut();
147            let res = f(
148                self.obj()
149                    .unsafe_cast_ref::<crate::Formatter>()
150                    .to_glib_none()
151                    .0,
152                timeline
153                    .unsafe_cast_ref::<crate::Timeline>()
154                    .to_glib_none()
155                    .0,
156                uri.to_glib_none().0,
157                overwrite.into_glib(),
158                &mut error,
159            );
160
161            if res == glib::ffi::GFALSE {
162                if error.is_null() {
163                    Err(glib::Error::new(
164                        gst::CoreError::Failed,
165                        "Save to uri failed",
166                    ))
167                } else {
168                    Err(from_glib_full(error))
169                }
170            } else {
171                Ok(())
172            }
173        }
174    }
175}
176
177impl<T: FormatterImpl> FormatterImplExt for T {}
178
179unsafe impl<T: FormatterImpl> IsSubclassable<T> for Formatter {
180    fn class_init(klass: &mut glib::Class<Self>) {
181        Self::parent_class_init::<T>(klass);
182        let klass = klass.as_mut();
183        klass.can_load_uri = Some(formatter_can_load_uri::<T>);
184        klass.load_from_uri = Some(formatter_load_from_uri::<T>);
185        klass.save_to_uri = Some(formatter_save_to_uri::<T>);
186    }
187}
188
189unsafe extern "C" fn formatter_can_load_uri<T: FormatterImpl>(
190    ptr: *mut ffi::GESFormatter,
191    uri: *const libc::c_char,
192    error: *mut *mut glib::ffi::GError,
193) -> glib::ffi::gboolean {
194    let instance = &*(ptr as *mut T::Instance);
195    let imp = instance.imp();
196
197    match imp.can_load_uri(glib::GString::from_glib_borrow(uri).as_str()) {
198        Err(err) => {
199            if !error.is_null() {
200                *error = err.into_glib_ptr();
201            }
202
203            glib::ffi::GFALSE
204        }
205        Ok(_) => glib::ffi::GTRUE,
206    }
207}
208
209unsafe extern "C" fn formatter_load_from_uri<T: FormatterImpl>(
210    ptr: *mut ffi::GESFormatter,
211    timeline: *mut ffi::GESTimeline,
212    uri: *const libc::c_char,
213    error: *mut *mut glib::ffi::GError,
214) -> glib::ffi::gboolean {
215    let instance = &*(ptr as *mut T::Instance);
216    let imp = instance.imp();
217    let timeline = from_glib_borrow(timeline);
218
219    match imp.load_from_uri(&timeline, glib::GString::from_glib_borrow(uri).as_str()) {
220        Err(err) => {
221            if !error.is_null() {
222                *error = err.into_glib_ptr();
223            }
224
225            glib::ffi::GFALSE
226        }
227        Ok(_) => glib::ffi::GTRUE,
228    }
229}
230
231unsafe extern "C" fn formatter_save_to_uri<T: FormatterImpl>(
232    ptr: *mut ffi::GESFormatter,
233    timeline: *mut ffi::GESTimeline,
234    uri: *const libc::c_char,
235    overwrite: glib::ffi::gboolean,
236    error: *mut *mut glib::ffi::GError,
237) -> glib::ffi::gboolean {
238    let instance = &*(ptr as *mut T::Instance);
239    let imp = instance.imp();
240    let timeline = from_glib_borrow(timeline);
241
242    match imp.save_to_uri(
243        &timeline,
244        glib::GString::from_glib_borrow(uri).as_str(),
245        from_glib(overwrite),
246    ) {
247        Err(err) => {
248            if !error.is_null() {
249                *error = err.into_glib_ptr();
250            }
251
252            glib::ffi::GFALSE
253        }
254        Ok(_) => glib::ffi::GTRUE,
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261    use crate::Formatter;
262
263    pub mod imp {
264        use super::*;
265
266        #[derive(Default)]
267        pub struct SimpleFormatter;
268
269        #[glib::object_subclass]
270        impl ObjectSubclass for SimpleFormatter {
271            const NAME: &'static str = "SimpleFormatter";
272            type Type = super::SimpleFormatter;
273            type ParentType = Formatter;
274        }
275        impl ObjectImpl for SimpleFormatter {}
276        impl FormatterImpl for SimpleFormatter {
277            fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
278                if uri.starts_with("ges:test") {
279                    Ok(())
280                } else {
281                    self.parent_can_load_uri(uri)
282                }
283            }
284
285            fn load_from_uri(
286                &self,
287                timeline: &crate::Timeline,
288                _uri: &str,
289            ) -> Result<(), glib::Error> {
290                timeline.append_layer();
291
292                Ok(())
293            }
294
295            fn save_to_uri(
296                &self,
297                timeline: &crate::Timeline,
298                uri: &str,
299                _overwrite: bool,
300            ) -> Result<(), glib::Error> {
301                unsafe { timeline.set_data("saved", uri.to_string()) };
302
303                Ok(())
304            }
305        }
306    }
307
308    glib::wrapper! {
309        pub struct SimpleFormatter(ObjectSubclass<imp::SimpleFormatter>) @extends Formatter, gst::Object;
310    }
311
312    impl SimpleFormatter {
313        pub fn new() -> Self {
314            glib::Object::builder().build()
315        }
316    }
317
318    impl Default for SimpleFormatter {
319        fn default() -> Self {
320            Self::new()
321        }
322    }
323
324    #[test]
325    fn test_formatter_subclass() {
326        crate::init().unwrap();
327
328        let formatter = SimpleFormatter::new();
329        formatter
330            .can_load_uri("ges:test:")
331            .expect("We can load anything...");
332
333        assert!(formatter.can_load_uri("nottest").is_err());
334
335        let timeline = crate::Timeline::new();
336        assert_eq!(timeline.layers().len(), 0);
337        #[allow(deprecated)]
338        formatter
339            .load_from_uri(&timeline, "test")
340            .expect("We can load anything...");
341        assert_eq!(timeline.layers().len(), 1);
342
343        unsafe {
344            assert_eq!(timeline.data::<Option<String>>("saved"), None);
345        }
346        #[allow(deprecated)]
347        formatter
348            .save_to_uri(&timeline, "test", false)
349            .expect("We can save anything...");
350        unsafe {
351            assert_eq!(
352                timeline.data::<String>("saved").unwrap().as_ref(),
353                &"test".to_string()
354            );
355        }
356
357        Formatter::register(
358            SimpleFormatter::static_type(),
359            "SimpleFormatter",
360            None,
361            None,
362            None,
363            1.0,
364            gst::Rank::PRIMARY,
365        );
366
367        let proj = crate::Project::new(Some("ges:test:"));
368        let timeline = proj
369            .extract()
370            .unwrap()
371            .downcast::<crate::Timeline>()
372            .unwrap();
373        assert_eq!(timeline.layers().len(), 1);
374
375        let proj = crate::Project::new(Some("ges:notest:"));
376        assert!(proj.extract().is_err());
377    }
378}