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}