Skip to main content

cpdb_sys/
settings.rs

1//! Safe wrappers around `cpdb_settings_t`, `cpdb_options_t`, and `cpdb_media_t`.
2
3use super::bindings as ffi;
4use crate::error::{CpdbError, Result};
5use std::ffi::CString;
6use std::ptr::NonNull;
7
8// ─── Settings ────────────────────────────────────────────────────────────────
9
10/// A free-standing cpdb settings collection.
11///
12/// Settings are key/value pairs that can be serialised, persisted, and
13/// later applied. For per-printer settings see [`crate::Printer`].
14pub struct Settings {
15    raw: NonNull<ffi::cpdb_settings_t>,
16}
17
18// SAFETY: `Settings` owns its `cpdb_settings_t *`. Moving it across threads
19// is fine because no other Rust handle aliases the same C object. Mutators
20// take `&mut self`, so shared `&Settings` access from multiple threads
21// only ever reads the C state, which is sound.
22unsafe impl Send for Settings {}
23unsafe impl Sync for Settings {}
24
25impl Settings {
26    /// Creates a new empty settings collection.
27    pub fn new() -> Result<Self> {
28        // SAFETY: `cpdbGetNewSettings` is a constructor with no preconditions.
29        let raw = unsafe { ffi::cpdbGetNewSettings() };
30        NonNull::new(raw)
31            .map(|raw| Self { raw })
32            .ok_or_else(|| CpdbError::BackendError("cpdbGetNewSettings returned null".into()))
33    }
34
35    /// Returns an independent deep copy.
36    pub fn try_clone(&self) -> Result<Self> {
37        let dst = Self::new()?;
38        // SAFETY: both pointers are valid, distinct, and live for the duration
39        // of this call.
40        unsafe { ffi::cpdbCopySettings(self.raw.as_ptr(), dst.raw.as_ptr()) };
41        Ok(dst)
42    }
43
44    /// Inserts or overwrites a setting.
45    pub fn add_setting(&mut self, key: &str, value: &str) -> Result<()> {
46        let key = CString::new(key)?;
47        let value = CString::new(value)?;
48        // SAFETY: pointer is non-null; the two `CString`s outlive the call.
49        unsafe { ffi::cpdbAddSetting(self.raw.as_ptr(), key.as_ptr(), value.as_ptr()) };
50        Ok(())
51    }
52
53    /// Removes a setting.
54    ///
55    /// Returns `Ok(true)` when the key existed before this call.
56    pub fn clear_setting(&mut self, key: &str) -> Result<bool> {
57        let key = CString::new(key)?;
58        // SAFETY: pointer is non-null; `CString` outlives the call.
59        let existed = unsafe { ffi::cpdbClearSetting(self.raw.as_ptr(), key.as_ptr()) };
60        Ok(existed != 0)
61    }
62
63    /// Persists this settings collection to the cpdb-managed config dir.
64    ///
65    /// The path is chosen by cpdb-libs (see `cpdbGetUserConfDir`) — callers
66    /// cannot override it.
67    pub fn save_to_disk(&self) -> Result<()> {
68        // SAFETY: pointer is non-null.
69        unsafe { ffi::cpdbSaveSettingsToDisk(self.raw.as_ptr()) };
70        Ok(())
71    }
72
73    /// Loads settings previously written via [`Settings::save_to_disk`].
74    pub fn read_from_disk() -> Result<Self> {
75        // SAFETY: `cpdbReadSettingsFromDisk` has no preconditions.
76        let raw = unsafe { ffi::cpdbReadSettingsFromDisk() };
77        NonNull::new(raw)
78            .map(|raw| Self { raw })
79            .ok_or_else(|| CpdbError::BackendError("cpdbReadSettingsFromDisk returned null".into()))
80    }
81
82    /// Returns the underlying raw pointer for use within this crate.
83    #[doc(hidden)]
84    pub fn as_raw(&self) -> *mut ffi::cpdb_settings_t {
85        self.raw.as_ptr()
86    }
87}
88
89impl Drop for Settings {
90    fn drop(&mut self) {
91        // SAFETY: we own the pointer; cpdb-libs is responsible for freeing
92        // the contained hash table.
93        unsafe { ffi::cpdbDeleteSettings(self.raw.as_ptr()) };
94    }
95}
96
97// ─── Options ─────────────────────────────────────────────────────────────────
98
99/// An empty cpdb options container.
100///
101/// Most callers do not need this directly — printer options should be read
102/// via [`crate::Printer::get_options_collection`]. This type exists for
103/// the rare case where you need a stand-alone `cpdb_options_t`.
104pub struct Options {
105    raw: NonNull<ffi::cpdb_options_t>,
106}
107
108// SAFETY: same reasoning as `Settings` — owned pointer, no shared aliasing.
109unsafe impl Send for Options {}
110unsafe impl Sync for Options {}
111
112impl Options {
113    /// Creates a new empty options object.
114    pub fn new() -> Result<Self> {
115        // SAFETY: `cpdbGetNewOptions` has no preconditions.
116        let raw = unsafe { ffi::cpdbGetNewOptions() };
117        NonNull::new(raw)
118            .map(|raw| Self { raw })
119            .ok_or_else(|| CpdbError::BackendError("cpdbGetNewOptions returned null".into()))
120    }
121
122    /// Returns the underlying raw pointer for use within this crate.
123    #[doc(hidden)]
124    pub fn as_raw(&self) -> *mut ffi::cpdb_options_t {
125        self.raw.as_ptr()
126    }
127}
128
129impl Drop for Options {
130    fn drop(&mut self) {
131        // SAFETY: we own the pointer.
132        unsafe { ffi::cpdbDeleteOptions(self.raw.as_ptr()) };
133    }
134}
135
136// ─── Media ───────────────────────────────────────────────────────────────────
137
138/// A stand-alone cpdb media descriptor.
139///
140/// Note that [`crate::Printer::get_media`] returns a *borrowed* media
141/// pointer that must not be wrapped in this type — only construct `Media`
142/// from a pointer you intend to free with `cpdbDeleteMedia`.
143pub struct Media {
144    raw: NonNull<ffi::cpdb_media_t>,
145}
146
147// SAFETY: same reasoning as `Settings`.
148unsafe impl Send for Media {}
149unsafe impl Sync for Media {}
150
151impl Media {
152    /// Wraps an owned `cpdb_media_t *`.
153    ///
154    /// # Safety
155    /// `raw` must be non-null, well-formed, and not aliased by any other
156    /// Rust or C handle; this `Media` will free it with `cpdbDeleteMedia`
157    /// on drop.
158    pub unsafe fn from_raw(raw: *mut ffi::cpdb_media_t) -> Result<Self> {
159        NonNull::new(raw)
160            .map(|raw| Self { raw })
161            .ok_or(CpdbError::NullPointer)
162    }
163
164    /// Returns the underlying raw pointer for use within this crate.
165    #[doc(hidden)]
166    pub fn as_raw(&self) -> *mut ffi::cpdb_media_t {
167        self.raw.as_ptr()
168    }
169}
170
171impl Drop for Media {
172    fn drop(&mut self) {
173        // SAFETY: we own the pointer.
174        unsafe { ffi::cpdbDeleteMedia(self.raw.as_ptr()) };
175    }
176}