nvdialog_rs/
string.rs

1/*
2 *  The MIT License (MIT)
3 *
4 *  Copyright (c) 2022-2025 Aggelos Tselios
5 *
6 *  Permission is hereby granted, free of charge, to any person obtaining a copy
7 *  of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 *  furnished to do so, subject to the following conditions:
12 *
13 *  The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25use std::{
26    ffi::{c_char, CStr}, fmt::Display, ptr::null
27};
28
29use crate::cstr;
30use nvdialog_sys::ffi::*;
31
32/// A Rust version of `NvdDynamicString`
33///
34/// A [`DynamicString`] is effectively a conversion of a heap allocated string designed to work with NvDialog
35/// that works with the Rust side. In essence, it's just another string type for Rust, but works with this crate instead.
36/// This type is useful since version v0.10 of NvDialog when the API was completely rewritten to use `NvdDynamicString`, a separate
37/// string type that addresses the limitations of working with raw character buffers and safety concerns.
38///
39#[derive(Debug)]
40pub struct DynamicString {
41    native: *mut NvdDynamicString,
42    this: String,
43}
44
45impl DynamicString {
46    /// Create a new `DynamicString` from optional UTF-8 data.
47    ///
48    /// - If `data` is `Some`, the contents are passed to the FFI (as a C string)
49    ///   and mirrored into `Self` as well.
50    /// - If `data` is `None`, a null is passed to `nvd_string_new`, which is
51    ///   expected (by the library) to produce an empty string.
52    /// 
53    /// Note that if you want to duplicate a string, you can use [`DynamicString::duplicate`] instead of
54    /// allocating a brand new one.
55    ///
56    /// ## Safety
57    /// The returned object can be modified safely, however note that any modifications are permanent and
58    /// if you received the `DynamicString` from another function, then trying to fetch it again will return your
59    /// modified string as the internal pointer was modified.
60    pub fn new<S: AsRef<str>>(data: Option<S>) -> Self {
61        if let Some(s) = data {
62            let string = cstr!(s.as_ref());
63            Self {
64                native: unsafe {
65                    let s = nvd_string_new(string.as_ptr());
66                    if s.is_null() {
67                        panic!("libnvdialog did not produce a valid NvdDynamicString!");
68                    }
69                    s
70                },
71                this: String::from(s.as_ref()),
72            }
73        } else {
74            Self {
75                native: unsafe {
76                    let s = nvd_string_new(null());
77                    if s.is_null() {
78                        panic!("libnvdialog did not produce a valid NvdDynamicString!");
79                    }
80                    s
81                },
82                this: String::new(),
83            }
84        }
85    }
86
87    /// Returns a borrowed pointer to the internal string's buffer.
88    ///
89    /// ## Safety
90    /// The returned pointer is only valid as long as `self` is. Additionally, if the internal data was corrupted,
91    /// this function may corrupt the program's memory, therefore make sure `self` was not internally modified beforehand.
92    /// ## Returns
93    /// A NULL-terminated, heap-allocated C-style string on success, or NULL if `nvd_string_to_cstr` failed.
94    #[inline]
95    pub unsafe fn as_ptr(&self) -> *const c_char {
96        assert!(!self.native.is_null());
97        nvd_string_to_cstr(self.native)
98    }
99
100    /// Create a deep copy of this `DynamicString`.
101    ///
102    /// Note that this function returns a truly unique `DynamicString` - It is safe to modify the returned value as
103    /// `self` does not point to it (and vice versa).
104    ///
105    /// ## Safety
106    /// This function is safe to use, unless the internal structure was corrupted beforehand, in which case `nvd_duplicate_string`
107    /// may return invalid or corrupt data. Make sure `self` has not been modified incorrectly before calling this function.
108    /// 
109    /// ## Returns
110    /// A deep copy of `self`, assuming no errors from the FFI-side.
111
112    #[inline]
113    pub fn duplicate(&self) -> Self {
114        Self {
115            native: unsafe { nvd_duplicate_string(self.native) },
116            this: self.this.clone(),
117        }
118    }
119
120    /// Returns a reference to the internal [`String`] that mirrors the FFI string to avoid extra allocations.
121    /// # Safety
122    /// This function is always safe to use. If you modify `self` correctly, then the contents are always mirrored.
123    #[inline]
124    pub fn as_str(&self) -> &str {
125        &self.this
126    }
127}
128
129impl From<*mut NvdDynamicString> for DynamicString {
130    fn from(value: *mut NvdDynamicString) -> Self {
131        assert!(!value.is_null());
132        Self {
133            native: value,
134            this: unsafe {
135                String::from(CStr::from_ptr(nvd_string_to_cstr(value)).to_string_lossy())
136            },
137        }
138    }
139}
140
141impl From<&str> for DynamicString {
142    fn from(s: &str) -> Self {
143        let string = cstr!(s);
144        Self {
145            native: unsafe { nvd_string_new(string.as_ptr()) },
146            this: s.to_owned(),
147        }
148    }
149}
150
151impl From<String> for DynamicString {
152    fn from(data: String) -> Self {
153        let string = cstr!(&*data);
154        Self {
155            native: unsafe { nvd_string_new(string.as_ptr()) },
156            this: data,
157        }
158    }
159}
160
161impl Display for DynamicString {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        write!(f, "{}", self.this)
164    }
165}
166
167impl Clone for DynamicString {
168    fn clone(&self) -> Self {
169        assert!(!self.native.is_null());
170        self.duplicate()
171    }
172}
173
174impl Drop for DynamicString {
175    fn drop(&mut self) {
176        unsafe { nvd_delete_string(self.native) }
177    }
178}