Skip to main content

gio/
dbus_interface_info.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ffi::CStr;
4
5use crate::ffi;
6use crate::{DBusInterfaceInfo, DBusPropertyInfo};
7use glib::translate::*;
8
9impl DBusInterfaceInfo {
10    pub fn name(&self) -> &str {
11        unsafe {
12            let c_obj = self.as_ptr();
13            let name = (*c_obj).name;
14            assert!(!name.is_null());
15            let c_str = CStr::from_ptr(name);
16            c_str.to_str().unwrap()
17        }
18    }
19
20    pub fn properties(&self) -> DBusInterfaceInfoPropertiesIter<'_> {
21        DBusInterfaceInfoPropertiesIter::new(self)
22    }
23}
24
25pub struct DBusInterfaceInfoPropertiesIter<'a> {
26    _stash: Stash<'a, *mut ffi::GDBusInterfaceInfo, DBusInterfaceInfo>,
27    next_property: *mut *mut ffi::GDBusPropertyInfo,
28}
29
30impl<'a> DBusInterfaceInfoPropertiesIter<'a> {
31    fn new(info: &'a DBusInterfaceInfo) -> Self {
32        let stash: Stash<*mut ffi::GDBusInterfaceInfo, _> = info.to_glib_none();
33        // SAFETY:
34        // * the stash is stored in the struct to keep the pointer valid
35        // * though not explicitly documented, this struct is assumed to be immutable after creation
36        //   (with the exception of ref_count of course). See usage in gdbusconnection.c
37        let next_property = unsafe { *stash.0 }.properties;
38        Self {
39            _stash: stash,
40            next_property,
41        }
42    }
43}
44
45impl<'a> Iterator for DBusInterfaceInfoPropertiesIter<'a> {
46    type Item = DBusPropertyInfo;
47
48    fn next(&mut self) -> Option<Self::Item> {
49        // SAFETY: `self.next_property` is a pointer to a NULL-terminated
50        // array of pointers to GDBusPropertyInfo.
51        unsafe {
52            assert!(!self.next_property.is_null());
53            let property = *self.next_property;
54            if !property.is_null() {
55                self.next_property = self.next_property.add(1);
56                Some(from_glib_none(property))
57            } else {
58                None
59            }
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use crate::DBusNodeInfo;
67
68    #[test]
69    fn iterate_properties() {
70        const XML: &str = r#"
71        <node>
72            <interface name='com.github.gtk_rs.Test'>
73                <property name='Name' type='s' access='read' />
74                <property name='Count' type='x' access='write' />
75            </interface>
76        </node>
77        "#;
78        let node_info = DBusNodeInfo::for_xml(XML).unwrap();
79        let interface = node_info
80            .lookup_interface("com.github.gtk_rs.Test")
81            .unwrap();
82
83        let mut properties = interface.properties();
84        let property = properties.next().unwrap();
85        assert_eq!("Name", property.name());
86        let property = properties.next().unwrap();
87        assert_eq!("Count", property.name());
88        assert!(properties.next().is_none());
89    }
90
91    #[test]
92    fn iterate_empty_properties() {
93        const XML: &str = r#"
94        <node>
95            <interface name='com.github.gtk_rs.Test' />
96        </node>
97        "#;
98        let node_info = DBusNodeInfo::for_xml(XML).unwrap();
99        let interface = node_info
100            .lookup_interface("com.github.gtk_rs.Test")
101            .unwrap();
102
103        let mut properties = interface.properties();
104        assert!(properties.next().is_none());
105    }
106
107    #[test]
108    fn iterator_is_sealed() {
109        const XML: &str = r#"
110        <node>
111            <interface name='com.github.gtk_rs.Test'>
112                <property name='Count' type='x' access='write' />
113            </interface>
114        </node>
115        "#;
116        let node_info = DBusNodeInfo::for_xml(XML).unwrap();
117        let interface = node_info
118            .lookup_interface("com.github.gtk_rs.Test")
119            .unwrap();
120
121        let mut properties = interface.properties();
122        let _property = properties.next().unwrap();
123        assert!(properties.next().is_none());
124
125        assert!(properties.next().is_none());
126    }
127}