Skip to main content

cpdb_sys/
options.rs

1//! Printer options (capabilities) from C `cpdb_options_t`.
2
3use crate::bindings as ffi;
4use crate::error::{CpdbError, Result};
5use crate::util;
6use glib_sys::{GHashTableIter, g_hash_table_iter_init, g_hash_table_iter_next};
7use std::mem::MaybeUninit;
8
9/// A single printer option with its supported choices.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct OptionInfo {
12    /// The option name, e.g. `"copies"` or `"sides"`.
13    pub name: String,
14    /// The default value as reported by the backend.
15    pub default_value: String,
16    /// The option group (e.g. `"General"`), or an empty string when unset.
17    pub group: String,
18    /// All values the printer supports for this option.
19    pub supported_values: Vec<String>,
20}
21
22/// An owned snapshot of every option in a `cpdb_options_t`.
23#[derive(Debug, Clone, Default)]
24pub struct OptionsCollection {
25    /// Every option discovered.
26    pub options: Vec<OptionInfo>,
27}
28
29impl OptionsCollection {
30    /// Builds an [`OptionsCollection`] by iterating `raw.table`.
31    ///
32    /// # Safety
33    ///
34    /// `raw` must be either null or a valid pointer to a fully initialised
35    /// `cpdb_options_t` whose `table` field is a valid `GHashTable*`.
36    pub unsafe fn from_raw(raw: *mut ffi::cpdb_options_t) -> Result<Self> {
37        if raw.is_null() {
38            return Err(CpdbError::NullPointer);
39        }
40
41        let table = unsafe { (*raw).table };
42
43        if table.is_null() {
44            return Ok(Self::default());
45        }
46
47        let mut options: Vec<OptionInfo> = Vec::new();
48
49        unsafe {
50            let mut iter = MaybeUninit::<GHashTableIter>::uninit();
51            g_hash_table_iter_init(iter.as_mut_ptr(), table as *mut glib_sys::GHashTable);
52            let mut iter = iter.assume_init();
53
54            let mut key: *mut libc::c_void = std::ptr::null_mut();
55            let mut value: *mut libc::c_void = std::ptr::null_mut();
56
57            while g_hash_table_iter_next(&mut iter, &mut key, &mut value) != 0 {
58                if value.is_null() {
59                    continue;
60                }
61                let opt = value as *mut ffi::cpdb_option_t;
62
63                let name = util::cstr_to_string((*opt).option_name).unwrap_or_default();
64                let default_value = util::cstr_to_string((*opt).default_value).unwrap_or_default();
65                let group = util::cstr_to_string((*opt).group_name).unwrap_or_default();
66
67                let mut supported_values: Vec<String> =
68                    Vec::with_capacity((*opt).num_supported as usize);
69
70                if !(*opt).supported_values.is_null() && (*opt).num_supported > 0 {
71                    for i in 0..((*opt).num_supported as usize) {
72                        let s_ptr = *(*opt).supported_values.add(i);
73                        if !s_ptr.is_null() {
74                            if let Ok(s) = util::cstr_to_string(s_ptr) {
75                                supported_values.push(s);
76                            }
77                        }
78                    }
79                }
80
81                options.push(OptionInfo {
82                    name,
83                    default_value,
84                    group,
85                    supported_values,
86                });
87            }
88        }
89
90        Ok(Self { options })
91    }
92
93    /// Returns the number of options in this collection.
94    pub fn len(&self) -> usize {
95        self.options.len()
96    }
97
98    /// Returns `true` if this collection has no options.
99    pub fn is_empty(&self) -> bool {
100        self.options.is_empty()
101    }
102
103    /// Finds an option by name (linear search).
104    pub fn get(&self, name: &str) -> Option<&OptionInfo> {
105        self.options.iter().find(|o| o.name == name)
106    }
107
108    /// Returns an iterator over all options.
109    pub fn iter(&self) -> impl Iterator<Item = &OptionInfo> {
110        self.options.iter()
111    }
112}