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