Skip to main content

gtk4/subclass/
buildable_parser.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use crate::ffi;
4use glib::{GString, translate::*};
5use std::{collections::HashMap, mem, ptr::NonNull};
6
7#[doc(alias = "GtkBuildableParseContext")]
8pub struct BuildableParseContext(NonNull<ffi::GtkBuildableParseContext>);
9
10impl BuildableParseContext {
11    pub(crate) fn from_raw(ctx: *mut ffi::GtkBuildableParseContext) -> Self {
12        Self(NonNull::new(ctx).expect("Null ParseContext"))
13    }
14
15    #[doc(alias = "gtk_buildable_parse_context_get_element")]
16    pub fn element(&self) -> Option<GString> {
17        unsafe {
18            from_glib_none(ffi::gtk_buildable_parse_context_get_element(
19                self.0.as_ptr(),
20            ))
21        }
22    }
23
24    #[doc(alias = "gtk_buildable_parse_context_get_element_stack")]
25    // rustdoc-stripper-ignore-next
26    /// Returns a list of tags with the last item being the currently open tag.
27    pub fn element_stack(&self) -> Vec<GString> {
28        unsafe {
29            let stack = ffi::gtk_buildable_parse_context_get_element_stack(self.0.as_ptr());
30            FromGlibPtrArrayContainerAsVec::from_glib_none_as_vec(stack)
31        }
32    }
33
34    pub fn position(&self) -> (i32, i32) {
35        unsafe {
36            let mut line_number = mem::MaybeUninit::uninit();
37            let mut char_number = mem::MaybeUninit::uninit();
38            ffi::gtk_buildable_parse_context_get_position(
39                self.0.as_ptr(),
40                line_number.as_mut_ptr(),
41                char_number.as_mut_ptr(),
42            );
43            (line_number.assume_init(), char_number.assume_init())
44        }
45    }
46}
47
48#[doc(alias = "GtkBuildableParser")]
49pub struct BuildableParser {
50    parser: ffi::GtkBuildableParser,
51    data: glib::ffi::gpointer,
52}
53
54impl BuildableParser {
55    pub(crate) unsafe fn from_raw_parts(
56        parser: ffi::GtkBuildableParser,
57        data: glib::ffi::gpointer,
58    ) -> Self {
59        Self { parser, data }
60    }
61
62    pub(crate) fn into_raw_parts(self) -> (ffi::GtkBuildableParser, glib::ffi::gpointer) {
63        (self.parser, self.data)
64    }
65
66    pub fn new<T: BuildableParserImpl>(data: T) -> Self {
67        Self {
68            parser: ffi::GtkBuildableParser {
69                start_element: Some(start_element_trampoline::<T>),
70                end_element: Some(end_element_trampoline::<T>),
71                text: Some(text_trampoline::<T>),
72                error: Some(error_trampoline::<T>),
73                padding: [
74                    std::ptr::null_mut(),
75                    std::ptr::null_mut(),
76                    std::ptr::null_mut(),
77                    std::ptr::null_mut(),
78                ],
79            },
80            data: Box::into_raw(Box::new(data)) as glib::ffi::gpointer,
81        }
82    }
83
84    // rustdoc-stripper-ignore-next
85    /// Takes ownership of parser data previously created by [`new()`](Self::new).
86    ///
87    /// # Safety
88    ///
89    /// The `data` pointer must have been created by `BuildableParser::new::<T>()`
90    /// and must not have been taken before.
91    pub unsafe fn take_data<T: BuildableParserImpl>(data: glib::ffi::gpointer) -> Box<T> {
92        unsafe { Box::from_raw(data as *mut T) }
93    }
94}
95
96// rustdoc-stripper-ignore-next
97/// # Safety
98///
99/// Implementations must not panic in any of the callbacks, as they are
100/// called from C code that cannot handle Rust panics.
101pub unsafe trait BuildableParserImpl: Sized + 'static {
102    fn start_element(
103        &mut self,
104        ctx: &BuildableParseContext,
105        element_name: &str,
106        attributes: HashMap<String, String>,
107    ) -> Result<(), glib::Error>;
108    fn end_element(
109        &mut self,
110        ctx: &BuildableParseContext,
111        element_name: &str,
112    ) -> Result<(), glib::Error>;
113    fn text(&mut self, ctx: &BuildableParseContext, text: &str) -> Result<(), glib::Error>;
114    fn error(&mut self, _ctx: &BuildableParseContext, _error: glib::Error) {}
115}
116
117unsafe extern "C" fn start_element_trampoline<T: BuildableParserImpl>(
118    ctx: *mut ffi::GtkBuildableParseContext,
119    element_name: *const libc::c_char,
120    attributes_names: *mut *const libc::c_char,
121    attributes_values: *mut *const libc::c_char,
122    user_data: glib::ffi::gpointer,
123    errorptr: *mut *mut glib::ffi::GError,
124) {
125    unsafe {
126        let ctx = BuildableParseContext::from_raw(ctx);
127        let name = from_glib_borrow::<_, GString>(element_name);
128        let attributes_names =
129            FromGlibPtrArrayContainerAsVec::from_glib_none_as_vec(attributes_names);
130        let attributes_values =
131            FromGlibPtrArrayContainerAsVec::from_glib_none_as_vec(attributes_values);
132        let attributes = attributes_names
133            .into_iter()
134            .zip(attributes_values)
135            .collect::<HashMap<String, String>>();
136        let parser_data = &mut *(user_data as *mut T);
137        match parser_data.start_element(&ctx, &name, attributes) {
138            Ok(_) => {
139                *errorptr = std::ptr::null_mut();
140            }
141            Err(err) => {
142                *errorptr = err.into_glib_ptr();
143            }
144        };
145    }
146}
147
148unsafe extern "C" fn end_element_trampoline<T: BuildableParserImpl>(
149    ctx: *mut ffi::GtkBuildableParseContext,
150    element_name: *const libc::c_char,
151    user_data: glib::ffi::gpointer,
152    errorptr: *mut *mut glib::ffi::GError,
153) {
154    unsafe {
155        let ctx = BuildableParseContext::from_raw(ctx);
156        let element_name = from_glib_borrow::<_, GString>(element_name);
157        let parser_data = &mut *(user_data as *mut T);
158        match parser_data.end_element(&ctx, &element_name) {
159            Ok(_) => {
160                *errorptr = std::ptr::null_mut();
161            }
162            Err(err) => {
163                *errorptr = err.into_glib_ptr();
164            }
165        }
166    }
167}
168
169unsafe extern "C" fn text_trampoline<T: BuildableParserImpl>(
170    ctx: *mut ffi::GtkBuildableParseContext,
171    text: *const libc::c_char,
172    _length: usize,
173    user_data: glib::ffi::gpointer,
174    errorptr: *mut *mut glib::ffi::GError,
175) {
176    unsafe {
177        let ctx = BuildableParseContext::from_raw(ctx);
178        let text = from_glib_borrow::<_, GString>(text);
179
180        let parser_data = &mut *(user_data as *mut T);
181        match parser_data.text(&ctx, &text) {
182            Ok(_) => {
183                *errorptr = std::ptr::null_mut();
184            }
185            Err(err) => {
186                *errorptr = err.into_glib_ptr();
187            }
188        }
189    }
190}
191
192unsafe extern "C" fn error_trampoline<T: BuildableParserImpl>(
193    ctx: *mut ffi::GtkBuildableParseContext,
194    errorptr: *mut glib::ffi::GError,
195    user_data: glib::ffi::gpointer,
196) {
197    unsafe {
198        let ctx = BuildableParseContext::from_raw(ctx);
199
200        let parser_data = &mut *(user_data as *mut T);
201        parser_data.error(&ctx, from_glib_full(errorptr));
202    }
203}