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::{prelude::*, subclass::prelude::*, translate::*, Error};
6
7use crate::{ffi, Cancellable, IOStream, InputStream, OutputStream};
8
9pub trait IOStreamImpl: ObjectImpl + IOStreamImplExt + Send {
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
37mod sealed {
38    pub trait Sealed {}
39    impl<T: super::IOStreamImplExt> Sealed for T {}
40}
41
42pub trait IOStreamImplExt: sealed::Sealed + ObjectSubclass {
43    fn parent_input_stream(&self) -> InputStream {
44        unsafe {
45            let data = Self::type_data();
46            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
47            let f = (*parent_class)
48                .get_input_stream
49                .expect("No parent class implementation for \"input_stream\"");
50            from_glib_none(f(self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0))
51        }
52    }
53
54    fn parent_output_stream(&self) -> OutputStream {
55        unsafe {
56            let data = Self::type_data();
57            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
58            let f = (*parent_class)
59                .get_output_stream
60                .expect("No parent class implementation for \"output_stream\"");
61            from_glib_none(f(self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0))
62        }
63    }
64
65    fn parent_close(&self, cancellable: Option<&Cancellable>) -> Result<(), Error> {
66        unsafe {
67            let data = Self::type_data();
68            let parent_class = data.as_ref().parent_class() as *mut ffi::GIOStreamClass;
69            let mut err = ptr::null_mut();
70            if let Some(f) = (*parent_class).close_fn {
71                if from_glib(f(
72                    self.obj().unsafe_cast_ref::<IOStream>().to_glib_none().0,
73                    cancellable.to_glib_none().0,
74                    &mut err,
75                )) {
76                    Ok(())
77                } else {
78                    Err(from_glib_full(err))
79                }
80            } else {
81                Ok(())
82            }
83        }
84    }
85}
86
87impl<T: IOStreamImpl> IOStreamImplExt for T {}
88
89unsafe impl<T: IOStreamImpl> IsSubclassable<T> for IOStream {
90    fn class_init(class: &mut ::glib::Class<Self>) {
91        Self::parent_class_init::<T>(class);
92
93        let klass = class.as_mut();
94        klass.get_input_stream = Some(stream_get_input_stream::<T>);
95        klass.get_output_stream = Some(stream_get_output_stream::<T>);
96        klass.close_fn = Some(stream_close::<T>);
97    }
98}
99
100unsafe extern "C" fn stream_get_input_stream<T: IOStreamImpl>(
101    ptr: *mut ffi::GIOStream,
102) -> *mut ffi::GInputStream {
103    let instance = &*(ptr as *mut T::Instance);
104    let imp = instance.imp();
105
106    let ret = imp.input_stream();
107
108    let instance = imp.obj();
109    // Ensure that a) the stream stays alive as long as the IO stream instance and
110    // b) that the same stream is returned every time. This is a requirement by the
111    // IO stream API.
112    let input_stream_quark = {
113        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
114        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-input-stream"))
115    };
116    if let Some(old_stream) = instance.qdata::<InputStream>(input_stream_quark) {
117        assert_eq!(
118            old_stream.as_ref(),
119            &ret,
120            "Did not return same input stream again"
121        );
122    }
123    instance.set_qdata(input_stream_quark, ret.clone());
124    ret.to_glib_none().0
125}
126
127unsafe extern "C" fn stream_get_output_stream<T: IOStreamImpl>(
128    ptr: *mut ffi::GIOStream,
129) -> *mut ffi::GOutputStream {
130    let instance = &*(ptr as *mut T::Instance);
131    let imp = instance.imp();
132
133    let ret = imp.output_stream();
134
135    let instance = imp.obj();
136    // Ensure that a) the stream stays alive as long as the IO stream instance and
137    // b) that the same stream is returned every time. This is a requirement by the
138    // IO stream API.
139    let output_stream_quark = {
140        static QUARK: OnceLock<glib::Quark> = OnceLock::new();
141        *QUARK.get_or_init(|| glib::Quark::from_str("gtk-rs-subclass-output-stream"))
142    };
143    if let Some(old_stream) = instance.qdata::<OutputStream>(output_stream_quark) {
144        assert_eq!(
145            old_stream.as_ref(),
146            &ret,
147            "Did not return same output stream again"
148        );
149    }
150    instance.set_qdata(output_stream_quark, ret.clone());
151    ret.to_glib_none().0
152}
153
154unsafe extern "C" fn stream_close<T: IOStreamImpl>(
155    ptr: *mut ffi::GIOStream,
156    cancellable: *mut ffi::GCancellable,
157    err: *mut *mut glib::ffi::GError,
158) -> glib::ffi::gboolean {
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}