gio/subclass/
io_stream.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ptr, sync::OnceLock};
4
5use glib::{Error, prelude::*, subclass::prelude::*, translate::*};
6
7use crate::{Cancellable, IOStream, InputStream, OutputStream, ffi};
8
9pub trait IOStreamImpl: Send + ObjectImpl + ObjectSubclass<Type: IsA<IOStream>> {
10    /// Gets the input stream for this object. This is used
11    /// for reading.
12    ///
13    /// # Returns
14    ///
15    /// a #GInputStream, owned by the #GIOStream.
16    /// Do not free.
17    fn input_stream(&self) -> InputStream {
18        self.parent_input_stream()
19    }
20
21    /// Gets the output stream for this object. This is used for
22    /// writing.
23    ///
24    /// # Returns
25    ///
26    /// a #GOutputStream, owned by the #GIOStream.
27    /// Do not free.
28    fn output_stream(&self) -> OutputStream {
29        self.parent_output_stream()
30    }
31
32    fn close(&self, cancellable: Option<&Cancellable>) -> Result<(), Error> {
33        self.parent_close(cancellable)
34    }
35}
36
37pub trait IOStreamImplExt: IOStreamImpl {
38    fn parent_input_stream(&self) -> InputStream {
39        unsafe {
40            let data = Self::type_data();
41            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
42            let f = (*parent_class)
43                .get_input_stream
44                .expect("No parent class implementation for \"input_stream\"");
45            from_glib_none(f(self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0))
46        }
47    }
48
49    fn parent_output_stream(&self) -> OutputStream {
50        unsafe {
51            let data = Self::type_data();
52            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
53            let f = (*parent_class)
54                .get_output_stream
55                .expect("No parent class implementation for \"output_stream\"");
56            from_glib_none(f(self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0))
57        }
58    }
59
60    fn parent_close(&self, cancellable: Option<&Cancellable>) -> Result<(), Error> {
61        unsafe {
62            let data = Self::type_data();
63            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
64            let mut err = ptr::null_mut();
65            if let Some(f) = (*parent_class).close_fn {
66                if from_glib(f(
67                    self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0,
68                    cancellable.to_glib_none().0,
69                    &mut err,
70                )) {
71                    Ok(())
72                } else {
73                    Err(from_glib_full(err))
74                }
75            } else {
76                Ok(())
77            }
78        }
79    }
80}
81
82impl<T: IOStreamImpl> IOStreamImplExt for T {}
83
84unsafe impl<T: IOStreamImpl> IsSubclassable<T> for IOStream {
85    fn class_init(class: &mut ::glib::Class<Self>) {
86        Self::parent_class_init::<T>(class);
87
88        let klass = class.as_mut();
89        klass.get_input_stream = Some(stream_get_input_stream::<T>);
90        klass.get_output_stream = Some(stream_get_output_stream::<T>);
91        klass.close_fn = Some(stream_close::<T>);
92    }
93}
94
95unsafe extern "C" fn stream_get_input_stream<T: IOStreamImpl>(
96    ptr: *mut ffi::GIOStream,
97) -> *mut ffi::GInputStream {
98    unsafe {
99        let instance = &*(ptr as *mut T::Instance);
100        let imp = instance.imp();
101
102        let ret = imp.input_stream();
103
104        let instance = imp.obj();
105        // Ensure that a) the stream stays alive as long as the IO stream instance and
106        // b) that the same stream is returned every time. This is a requirement by the
107        // IO stream API.
108        let input_stream_quark = {
109            static QUARK: OnceLock<glib::Quark> = OnceLock::new();
110            *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-input-stream"))
111        };
112        if let Some(old_stream) = instance.qdata::<InputStream>(input_stream_quark) {
113            assert_eq!(
114                old_stream.as_ref(),
115                &ret,
116                "Did not return same input stream again"
117            );
118        }
119        instance.set_qdata(input_stream_quark, ret.clone());
120        ret.to_glib_none().0
121    }
122}
123
124unsafe extern "C" fn stream_get_output_stream<T: IOStreamImpl>(
125    ptr: *mut ffi::GIOStream,
126) -> *mut ffi::GOutputStream {
127    unsafe {
128        let instance = &*(ptr as *mut T::Instance);
129        let imp = instance.imp();
130
131        let ret = imp.output_stream();
132
133        let instance = imp.obj();
134        // Ensure that a) the stream stays alive as long as the IO stream instance and
135        // b) that the same stream is returned every time. This is a requirement by the
136        // IO stream API.
137        let output_stream_quark = {
138            static QUARK: OnceLock<glib::Quark> = OnceLock::new();
139            *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-output-stream"))
140        };
141        if let Some(old_stream) = instance.qdata::<OutputStream>(output_stream_quark) {
142            assert_eq!(
143                old_stream.as_ref(),
144                &ret,
145                "Did not return same output stream again"
146            );
147        }
148        instance.set_qdata(output_stream_quark, ret.clone());
149        ret.to_glib_none().0
150    }
151}
152
153unsafe extern "C" fn stream_close<T: IOStreamImpl>(
154    ptr: *mut ffi::GIOStream,
155    cancellable: *mut ffi::GCancellable,
156    err: *mut *mut glib::ffi::GError,
157) -> glib::ffi::gboolean {
158    unsafe {
159        let instance = &*(ptr as *mut T::Instance);
160        let imp = instance.imp();
161
162        match imp.close(
163            Option::<Cancellable>::from_glib_borrow(cancellable)
164                .as_ref()
165                .as_ref(),
166        ) {
167            Ok(_) => glib::ffi::GTRUE,
168            Err(e) => {
169                if !err.is_null() {
170                    *err = e.into_glib_ptr();
171                }
172                glib::ffi::GFALSE
173            }
174        }
175    }
176}