Skip to main content

mtl_foundation/
bundle.rs

1//! Bundle type for Foundation.
2//!
3//! Corresponds to `Foundation/NSBundle.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! class Bundle : public Referencing<Bundle> {
10//! public:
11//!     static Bundle*      mainBundle();
12//!     static Bundle*      bundle(const class String* pPath);
13//!     static Bundle*      bundle(const class URL* pURL);
14//!     static class Array* allBundles();
15//!     static class Array* allFrameworks();
16//!     static Bundle*      alloc();
17//!     Bundle*             init(const class String* pPath);
18//!     Bundle*             init(const class URL* pURL);
19//!     bool                load();
20//!     bool                unload();
21//!     bool                isLoaded() const;
22//!     bool                preflightAndReturnError(class Error** pError) const;
23//!     bool                loadAndReturnError(class Error** pError);
24//!     // ... many path/URL methods
25//!     class String*       bundleIdentifier() const;
26//!     class Dictionary*   infoDictionary() const;
27//!     class String*       localizedString(...) const;
28//! };
29//! }
30//! ```
31
32use std::ffi::c_void;
33use std::ptr::NonNull;
34
35use mtl_sys::{class, msg_send_0, msg_send_1, msg_send_3, sel};
36
37use crate::array::Array;
38use crate::dictionary::Dictionary;
39use crate::error::Error;
40use crate::object::{Object, Referencing};
41use crate::string::String;
42use crate::url::Url;
43
44/// An Objective-C bundle object.
45///
46/// C++ equivalent: `NS::Bundle`
47#[repr(transparent)]
48#[derive(Clone)]
49pub struct Bundle(NonNull<c_void>);
50
51impl Bundle {
52    /// Get the main bundle.
53    ///
54    /// C++ equivalent: `static Bundle* mainBundle()`
55    #[inline]
56    pub fn main_bundle() -> Option<Self> {
57        unsafe {
58            let ptr: *mut c_void = msg_send_0(class!(NSBundle).as_ptr(), sel!(mainBundle));
59            Self::from_ptr(ptr)
60        }
61    }
62
63    /// Get a bundle with the specified path.
64    ///
65    /// C++ equivalent: `static Bundle* bundle(const class String* pPath)`
66    #[inline]
67    pub fn bundle_with_path(path: &String) -> Option<Self> {
68        unsafe {
69            let ptr: *mut c_void = msg_send_1(
70                class!(NSBundle).as_ptr(),
71                sel!(bundleWithPath:),
72                path.as_ptr(),
73            );
74            Self::from_ptr(ptr)
75        }
76    }
77
78    /// Get a bundle with the specified URL.
79    ///
80    /// C++ equivalent: `static Bundle* bundle(const class URL* pURL)`
81    #[inline]
82    pub fn bundle_with_url(url: &Url) -> Option<Self> {
83        unsafe {
84            let ptr: *mut c_void = msg_send_1(
85                class!(NSBundle).as_ptr(),
86                sel!(bundleWithURL:),
87                url.as_ptr(),
88            );
89            Self::from_ptr(ptr)
90        }
91    }
92
93    /// Get all bundles.
94    ///
95    /// C++ equivalent: `static class Array* allBundles()`
96    #[inline]
97    pub fn all_bundles() -> *mut Array<Bundle> {
98        unsafe { msg_send_0(class!(NSBundle).as_ptr(), sel!(allBundles)) }
99    }
100
101    /// Get all frameworks.
102    ///
103    /// C++ equivalent: `static class Array* allFrameworks()`
104    #[inline]
105    pub fn all_frameworks() -> *mut Array<Bundle> {
106        unsafe { msg_send_0(class!(NSBundle).as_ptr(), sel!(allFrameworks)) }
107    }
108
109    /// Allocate a new bundle.
110    ///
111    /// C++ equivalent: `static Bundle* alloc()`
112    #[inline]
113    pub fn alloc() -> Option<Self> {
114        unsafe {
115            let ptr: *mut c_void = msg_send_0(class!(NSBundle).as_ptr(), sel!(alloc));
116            Self::from_ptr(ptr)
117        }
118    }
119
120    /// Initialize with a path.
121    ///
122    /// C++ equivalent: `Bundle* init(const class String* pPath)`
123    #[inline]
124    pub fn init_with_path(&self, path: &String) -> Option<Self> {
125        unsafe {
126            let ptr: *mut c_void = msg_send_1(self.as_ptr(), sel!(initWithPath:), path.as_ptr());
127            Self::from_ptr(ptr)
128        }
129    }
130
131    /// Initialize with a URL.
132    ///
133    /// C++ equivalent: `Bundle* init(const class URL* pURL)`
134    #[inline]
135    pub fn init_with_url(&self, url: &Url) -> Option<Self> {
136        unsafe {
137            let ptr: *mut c_void = msg_send_1(self.as_ptr(), sel!(initWithURL:), url.as_ptr());
138            Self::from_ptr(ptr)
139        }
140    }
141
142    /// Load the bundle.
143    ///
144    /// C++ equivalent: `bool load()`
145    #[inline]
146    pub fn load(&self) -> bool {
147        unsafe { msg_send_0(self.as_ptr(), sel!(load)) }
148    }
149
150    /// Unload the bundle.
151    ///
152    /// C++ equivalent: `bool unload()`
153    #[inline]
154    pub fn unload(&self) -> bool {
155        unsafe { msg_send_0(self.as_ptr(), sel!(unload)) }
156    }
157
158    /// Check if the bundle is loaded.
159    ///
160    /// C++ equivalent: `bool isLoaded() const`
161    #[inline]
162    pub fn is_loaded(&self) -> bool {
163        unsafe { msg_send_0(self.as_ptr(), sel!(isLoaded)) }
164    }
165
166    /// Preflight the bundle and return any error.
167    ///
168    /// C++ equivalent: `bool preflightAndReturnError(class Error** pError) const`
169    #[inline]
170    pub fn preflight_and_return_error(&self, error: *mut *mut Error) -> bool {
171        unsafe { msg_send_1(self.as_ptr(), sel!(preflightAndReturnError:), error) }
172    }
173
174    /// Load the bundle and return any error.
175    ///
176    /// C++ equivalent: `bool loadAndReturnError(class Error** pError)`
177    #[inline]
178    pub fn load_and_return_error(&self, error: *mut *mut Error) -> bool {
179        unsafe { msg_send_1(self.as_ptr(), sel!(loadAndReturnError:), error) }
180    }
181
182    /// Get the bundle URL.
183    ///
184    /// C++ equivalent: `class URL* bundleURL() const`
185    #[inline]
186    pub fn bundle_url(&self) -> *mut Url {
187        unsafe { msg_send_0(self.as_ptr(), sel!(bundleURL)) }
188    }
189
190    /// Get the resource URL.
191    ///
192    /// C++ equivalent: `class URL* resourceURL() const`
193    #[inline]
194    pub fn resource_url(&self) -> *mut Url {
195        unsafe { msg_send_0(self.as_ptr(), sel!(resourceURL)) }
196    }
197
198    /// Get the executable URL.
199    ///
200    /// C++ equivalent: `class URL* executableURL() const`
201    #[inline]
202    pub fn executable_url(&self) -> *mut Url {
203        unsafe { msg_send_0(self.as_ptr(), sel!(executableURL)) }
204    }
205
206    /// Get the URL for an auxiliary executable.
207    ///
208    /// C++ equivalent: `class URL* URLForAuxiliaryExecutable(const class String* pExecutableName) const`
209    #[inline]
210    pub fn url_for_auxiliary_executable(&self, name: &String) -> *mut Url {
211        unsafe {
212            msg_send_1(
213                self.as_ptr(),
214                sel!(URLForAuxiliaryExecutable:),
215                name.as_ptr(),
216            )
217        }
218    }
219
220    /// Get the private frameworks URL.
221    #[inline]
222    pub fn private_frameworks_url(&self) -> *mut Url {
223        unsafe { msg_send_0(self.as_ptr(), sel!(privateFrameworksURL)) }
224    }
225
226    /// Get the shared frameworks URL.
227    #[inline]
228    pub fn shared_frameworks_url(&self) -> *mut Url {
229        unsafe { msg_send_0(self.as_ptr(), sel!(sharedFrameworksURL)) }
230    }
231
232    /// Get the shared support URL.
233    #[inline]
234    pub fn shared_support_url(&self) -> *mut Url {
235        unsafe { msg_send_0(self.as_ptr(), sel!(sharedSupportURL)) }
236    }
237
238    /// Get the built-in plug-ins URL.
239    #[inline]
240    pub fn built_in_plug_ins_url(&self) -> *mut Url {
241        unsafe { msg_send_0(self.as_ptr(), sel!(builtInPlugInsURL)) }
242    }
243
244    /// Get the App Store receipt URL.
245    #[inline]
246    pub fn app_store_receipt_url(&self) -> *mut Url {
247        unsafe { msg_send_0(self.as_ptr(), sel!(appStoreReceiptURL)) }
248    }
249
250    /// Get the bundle path.
251    ///
252    /// C++ equivalent: `class String* bundlePath() const`
253    #[inline]
254    pub fn bundle_path(&self) -> *mut String {
255        unsafe { msg_send_0(self.as_ptr(), sel!(bundlePath)) }
256    }
257
258    /// Get the resource path.
259    #[inline]
260    pub fn resource_path(&self) -> *mut String {
261        unsafe { msg_send_0(self.as_ptr(), sel!(resourcePath)) }
262    }
263
264    /// Get the executable path.
265    #[inline]
266    pub fn executable_path(&self) -> *mut String {
267        unsafe { msg_send_0(self.as_ptr(), sel!(executablePath)) }
268    }
269
270    /// Get the path for an auxiliary executable.
271    #[inline]
272    pub fn path_for_auxiliary_executable(&self, name: &String) -> *mut String {
273        unsafe {
274            msg_send_1(
275                self.as_ptr(),
276                sel!(pathForAuxiliaryExecutable:),
277                name.as_ptr(),
278            )
279        }
280    }
281
282    /// Get the private frameworks path.
283    #[inline]
284    pub fn private_frameworks_path(&self) -> *mut String {
285        unsafe { msg_send_0(self.as_ptr(), sel!(privateFrameworksPath)) }
286    }
287
288    /// Get the shared frameworks path.
289    #[inline]
290    pub fn shared_frameworks_path(&self) -> *mut String {
291        unsafe { msg_send_0(self.as_ptr(), sel!(sharedFrameworksPath)) }
292    }
293
294    /// Get the shared support path.
295    #[inline]
296    pub fn shared_support_path(&self) -> *mut String {
297        unsafe { msg_send_0(self.as_ptr(), sel!(sharedSupportPath)) }
298    }
299
300    /// Get the built-in plug-ins path.
301    #[inline]
302    pub fn built_in_plug_ins_path(&self) -> *mut String {
303        unsafe { msg_send_0(self.as_ptr(), sel!(builtInPlugInsPath)) }
304    }
305
306    /// Get the bundle identifier.
307    ///
308    /// C++ equivalent: `class String* bundleIdentifier() const`
309    #[inline]
310    pub fn bundle_identifier(&self) -> *mut String {
311        unsafe { msg_send_0(self.as_ptr(), sel!(bundleIdentifier)) }
312    }
313
314    /// Get the info dictionary.
315    ///
316    /// C++ equivalent: `class Dictionary* infoDictionary() const`
317    #[inline]
318    pub fn info_dictionary(&self) -> *mut Dictionary {
319        unsafe { msg_send_0(self.as_ptr(), sel!(infoDictionary)) }
320    }
321
322    /// Get the localized info dictionary.
323    ///
324    /// C++ equivalent: `class Dictionary* localizedInfoDictionary() const`
325    #[inline]
326    pub fn localized_info_dictionary(&self) -> *mut Dictionary {
327        unsafe { msg_send_0(self.as_ptr(), sel!(localizedInfoDictionary)) }
328    }
329
330    /// Get an object from the info dictionary.
331    ///
332    /// C++ equivalent: `class Object* objectForInfoDictionaryKey(const class String* pKey)`
333    #[inline]
334    pub fn object_for_info_dictionary_key(&self, key: &String) -> *mut Object {
335        unsafe {
336            msg_send_1(
337                self.as_ptr(),
338                sel!(objectForInfoDictionaryKey:),
339                key.as_ptr(),
340            )
341        }
342    }
343
344    /// Get a localized string.
345    ///
346    /// C++ equivalent: `class String* localizedString(const class String* pKey, const class String* pValue = nullptr, const class String* pTableName = nullptr) const`
347    #[inline]
348    pub fn localized_string(
349        &self,
350        key: &String,
351        value: *const String,
352        table_name: *const String,
353    ) -> *mut String {
354        unsafe {
355            msg_send_3(
356                self.as_ptr(),
357                sel!(localizedStringForKey:value:table:),
358                key.as_ptr(),
359                value,
360                table_name,
361            )
362        }
363    }
364
365    /// Create a Bundle from a raw pointer.
366    ///
367    /// # Safety
368    ///
369    /// The pointer must be a valid Objective-C NSBundle object.
370    #[inline]
371    pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
372        NonNull::new(ptr).map(Self)
373    }
374}
375
376impl Referencing for Bundle {
377    #[inline]
378    fn as_ptr(&self) -> *const c_void {
379        self.0.as_ptr()
380    }
381}
382
383unsafe impl Send for Bundle {}
384unsafe impl Sync for Bundle {}
385
386impl std::fmt::Debug for Bundle {
387    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388        f.debug_struct("Bundle").field("ptr", &self.0).finish()
389    }
390}
391
392// Helper functions matching C++ free functions
393
394/// Get a localized string from the main bundle.
395///
396/// C++ equivalent: `NS::LocalizedString(const String* pKey, const String*)`
397#[inline]
398pub fn localized_string(key: &String) -> *mut String {
399    if let Some(bundle) = Bundle::main_bundle() {
400        bundle.localized_string(key, std::ptr::null(), std::ptr::null())
401    } else {
402        std::ptr::null_mut()
403    }
404}
405
406/// Get a localized string from a table in the main bundle.
407///
408/// C++ equivalent: `NS::LocalizedStringFromTable(...)`
409#[inline]
410pub fn localized_string_from_table(key: &String, table: &String) -> *mut String {
411    if let Some(bundle) = Bundle::main_bundle() {
412        bundle.localized_string(key, std::ptr::null(), table)
413    } else {
414        std::ptr::null_mut()
415    }
416}
417
418/// Get a localized string from a table in a bundle.
419///
420/// C++ equivalent: `NS::LocalizedStringFromTableInBundle(...)`
421#[inline]
422pub fn localized_string_from_table_in_bundle(
423    key: &String,
424    table: &String,
425    bundle: &Bundle,
426) -> *mut String {
427    bundle.localized_string(key, std::ptr::null(), table)
428}
429
430/// Get a localized string with a default value.
431///
432/// C++ equivalent: `NS::LocalizedStringWithDefaultValue(...)`
433#[inline]
434pub fn localized_string_with_default_value(
435    key: &String,
436    table: &String,
437    bundle: &Bundle,
438    value: &String,
439) -> *mut String {
440    bundle.localized_string(key, value, table)
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446
447    #[test]
448    fn test_bundle_size() {
449        assert_eq!(
450            std::mem::size_of::<Bundle>(),
451            std::mem::size_of::<*mut c_void>()
452        );
453    }
454}