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