pango/
script_iter.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{marker::PhantomData, mem, ptr};
4
5use glib::{translate::*, GStr};
6
7use crate::{ffi, Script};
8
9/// A [`ScriptIter`][crate::ScriptIter] is used to iterate through a string
10/// and identify ranges in different scripts.
11#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct ScriptIter<'text> {
13    ptr: ptr::NonNull<ffi::PangoScriptIter>,
14    text: PhantomData<&'text GStr>,
15}
16
17#[cfg(feature = "v1_44")]
18#[cfg_attr(docsrs, doc(cfg(feature = "v1_44")))]
19impl Clone for ScriptIter<'_> {
20    #[inline]
21    fn clone(&self) -> Self {
22        let ptr = unsafe {
23            ptr::NonNull::new_unchecked(glib::gobject_ffi::g_boxed_copy(
24                ffi::pango_script_iter_get_type(),
25                self.ptr.as_ptr() as *mut _,
26            ) as *mut _)
27        };
28        Self {
29            ptr,
30            text: PhantomData,
31        }
32    }
33}
34
35impl Drop for ScriptIter<'_> {
36    #[inline]
37    fn drop(&mut self) {
38        unsafe {
39            ffi::pango_script_iter_free(self.ptr.as_ptr());
40        }
41    }
42}
43
44#[cfg(feature = "v1_44")]
45#[cfg_attr(docsrs, doc(cfg(feature = "v1_44")))]
46impl glib::prelude::StaticType for ScriptIter<'_> {
47    #[inline]
48    fn static_type() -> glib::Type {
49        unsafe { from_glib(ffi::pango_script_iter_get_type()) }
50    }
51}
52
53impl<'text> ScriptIter<'text> {
54    #[doc(alias = "pango_script_iter_new")]
55    pub fn new(text: impl AsRef<GStr> + 'text) -> Self {
56        let text = text.as_ref();
57        let length = text.len() as i32;
58        unsafe { from_glib_full(ffi::pango_script_iter_new(text.as_ptr(), length)) }
59    }
60
61    #[doc(alias = "pango_script_iter_get_range")]
62    #[doc(alias = "get_range")]
63    pub fn range(&mut self) -> (&'text GStr, &'text GStr, Script) {
64        unsafe {
65            let mut start = ptr::null();
66            let mut end = ptr::null();
67            let mut script = mem::MaybeUninit::uninit();
68            ffi::pango_script_iter_get_range(
69                self.to_glib_none_mut().0,
70                &mut start,
71                &mut end,
72                script.as_mut_ptr(),
73            );
74            (
75                GStr::from_ptr(start),
76                GStr::from_ptr(end),
77                from_glib(script.assume_init()),
78            )
79        }
80    }
81
82    #[doc(alias = "pango_script_iter_next")]
83    #[doc(alias = "next")]
84    pub fn next_range(&mut self) -> bool {
85        unsafe { from_glib(ffi::pango_script_iter_next(self.to_glib_none_mut().0)) }
86    }
87}
88
89impl<'text> IntoIterator for ScriptIter<'text> {
90    type Item = (&'text GStr, &'text GStr, Script);
91    type IntoIter = ScriptIntoIter<'text>;
92
93    fn into_iter(self) -> Self::IntoIter {
94        ScriptIntoIter(Some(self))
95    }
96}
97
98#[cfg_attr(feature = "v1_44", derive(Clone))]
99#[derive(Debug)]
100#[repr(transparent)]
101pub struct ScriptIntoIter<'text>(Option<ScriptIter<'text>>);
102
103impl<'text> Iterator for ScriptIntoIter<'text> {
104    type Item = (&'text GStr, &'text GStr, Script);
105
106    fn next(&mut self) -> Option<Self::Item> {
107        if let Some(iter) = &mut self.0 {
108            let attrs = iter.range();
109            if !iter.next_range() {
110                self.0 = None;
111            }
112            Some(attrs)
113        } else {
114            None
115        }
116    }
117}
118
119impl std::iter::FusedIterator for ScriptIntoIter<'_> {}
120
121#[doc(hidden)]
122impl<'a, 'text> ToGlibPtr<'a, *const ffi::PangoScriptIter> for ScriptIter<'text>
123where
124    'text: 'a,
125{
126    type Storage = PhantomData<&'a Self>;
127    #[inline]
128    fn to_glib_none(&'a self) -> Stash<'a, *const ffi::PangoScriptIter, Self> {
129        Stash(self.ptr.as_ptr() as *const _, PhantomData)
130    }
131}
132
133#[doc(hidden)]
134impl<'a, 'text> ToGlibPtrMut<'a, *mut ffi::PangoScriptIter> for ScriptIter<'text>
135where
136    'text: 'a,
137{
138    type Storage = PhantomData<&'a mut Self>;
139    #[inline]
140    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::PangoScriptIter, Self> {
141        StashMut(self.ptr.as_ptr(), PhantomData)
142    }
143}
144
145#[doc(hidden)]
146impl FromGlibPtrFull<*mut ffi::PangoScriptIter> for ScriptIter<'_> {
147    #[inline]
148    unsafe fn from_glib_full(ptr: *mut ffi::PangoScriptIter) -> Self {
149        Self {
150            ptr: ptr::NonNull::new_unchecked(ptr),
151            text: PhantomData,
152        }
153    }
154}
155
156#[cfg(test)]
157mod tests {
158    const SCRIPTS: &glib::GStr = glib::gstr!(
159        "\u{0020}\u{0946}\u{0939}\u{093F}\u{0928}\u{094D}\u{0926}\u{0940}\u{0020}\
160         \u{0627}\u{0644}\u{0639}\u{0631}\u{0628}\u{064A}\u{0629}\u{0020}"
161    );
162
163    #[test]
164    fn script_iter() {
165        let iter = super::ScriptIter::new(SCRIPTS);
166        let scripts = iter.into_iter().collect::<Vec<_>>();
167        assert_eq!(scripts.len(), 2);
168        assert_eq!(scripts[0].0, SCRIPTS);
169        assert_eq!(scripts[0].1, &SCRIPTS[23..]);
170        assert_eq!(scripts[0].2, crate::Script::Devanagari);
171        assert_eq!(scripts[1].0, &SCRIPTS[23..]);
172        assert_eq!(scripts[1].1, &SCRIPTS[38..]);
173        assert_eq!(scripts[1].2, crate::Script::Arabic);
174    }
175}