nvim_types/
string.rs

1use std::borrow::Cow;
2use std::ffi::{c_char, c_int, OsStr};
3use std::mem::ManuallyDrop;
4use std::path::PathBuf;
5use std::string::{self, String as StdString};
6use std::{fmt, slice, str};
7
8use lua::{ffi::*, Poppable, Pushable};
9use luajit_bindings as lua;
10
11use crate::NonOwning;
12
13// Neovim's `String`s:
14//   - are null-terminated;
15//   - *can* contain null bytes (confirmed by bfredl on matrix);
16//   - they store a `size` field just like Rust strings, which *doesn't*
17//     include the last `\0`;
18//   - unlike Rust strings, they are *not* guaranteed to always contain valid
19//     UTF-8 byte sequences;
20//
21// See https://github.com/neovim/neovim/blob/master/src/nvim/api/private/helpers.c#L478
22// for how a C string gets converted into a Neovim string.
23//
24// https://github.com/neovim/neovim/blob/master/src/nvim/api/private/defs.h#L77
25//
26/// Binding to the string type used by Neovim.
27#[derive(Eq, Ord, PartialOrd, Hash)]
28#[repr(C)]
29pub struct String {
30    pub(crate) data: *mut c_char,
31    pub(crate) size: usize,
32}
33
34impl String {
35    #[inline]
36    /// Creates a new empty string.
37    pub fn new() -> Self {
38        Self { data: std::ptr::null_mut(), size: 0 }
39    }
40
41    /// Creates a [`String`] from a byte vector.
42    #[inline]
43    pub fn from_bytes(mut vec: Vec<u8>) -> Self {
44        vec.reserve_exact(1);
45        Vec::push(&mut vec, 0);
46
47        let size = vec.len() - 1;
48        let data = vec.leak().as_mut_ptr() as *mut c_char;
49
50        Self { data, size }
51    }
52
53    /// Returns `true` if the `String` has a length of zero, and `false`
54    /// otherwise.
55    #[inline]
56    pub const fn is_empty(&self) -> bool {
57        self.len() == 0
58    }
59
60    /// Returns the byte length of the `String`, *not* including the final null
61    /// byte.
62    #[inline]
63    pub const fn len(&self) -> usize {
64        self.size
65    }
66
67    /// Returns a pointer pointing to the byte of the string.
68    #[inline]
69    pub const fn as_ptr(&self) -> *const c_char {
70        self.data as *const c_char
71    }
72
73    /// Returns a byte slice of this `String`'s contents.
74    #[inline]
75    pub fn as_bytes(&self) -> &[u8] {
76        if self.data.is_null() {
77            &[]
78        } else {
79            unsafe { slice::from_raw_parts(self.data as *const u8, self.size) }
80        }
81    }
82
83    /// Returns a string slice of this `String`'s contents. Fails if it doesn't
84    /// contain a valid UTF-8 byte sequence.
85    #[inline]
86    pub fn as_str(&self) -> Result<&str, str::Utf8Error> {
87        str::from_utf8(self.as_bytes())
88    }
89
90    /// Converts the `String` into Rust's `std::string::String`. If it already
91    /// holds a valid UTF-8 byte sequence no allocation is made. If it doesn't
92    /// the `String` is copied and all invalid sequences are replaced with `�`.
93    #[inline]
94    pub fn to_string_lossy(&self) -> Cow<'_, str> {
95        StdString::from_utf8_lossy(self.as_bytes())
96    }
97
98    /// Converts the `String` into a byte vector, consuming it.
99    #[inline]
100    pub fn into_bytes(self) -> Vec<u8> {
101        if self.data.is_null() {
102            Vec::new()
103        } else {
104            unsafe {
105                let mdrop = ManuallyDrop::new(self);
106                Vec::from_raw_parts(
107                    mdrop.data.cast::<u8>(),
108                    mdrop.size,
109                    mdrop.size,
110                )
111            }
112        }
113    }
114
115    /// Converts the `String` into Rust's `std::string::String`, consuming it.
116    /// Fails if it doesn't contain a valid UTF-8 byte sequence.
117    #[inline]
118    pub fn into_string(self) -> Result<StdString, string::FromUtf8Error> {
119        StdString::from_utf8(self.into_bytes())
120    }
121
122    /// Makes a non-owning version of this `String`.
123    #[inline]
124    #[doc(hidden)]
125    pub fn non_owning(&self) -> NonOwning<'_, String> {
126        NonOwning::new(Self { ..*self })
127    }
128}
129
130impl Default for String {
131    #[inline]
132    fn default() -> Self {
133        Self::new()
134    }
135}
136
137impl fmt::Debug for String {
138    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139        fmt::Display::fmt(self, f)
140    }
141}
142
143impl fmt::Display for String {
144    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
145        f.write_str(&self.to_string_lossy())
146    }
147}
148
149impl Clone for String {
150    fn clone(&self) -> Self {
151        Self::from_bytes(self.as_bytes().to_owned())
152    }
153}
154
155impl Drop for String {
156    fn drop(&mut self) {
157        // One extra for null terminator.
158        let _ = unsafe {
159            Vec::from_raw_parts(self.data, self.size + 1, self.size + 1)
160        };
161    }
162}
163
164impl From<StdString> for String {
165    #[inline]
166    fn from(string: StdString) -> Self {
167        Self::from_bytes(string.into_bytes())
168    }
169}
170
171impl From<&str> for String {
172    #[inline]
173    fn from(str: &str) -> Self {
174        Self::from_bytes(str.as_bytes().to_owned())
175    }
176}
177
178impl From<char> for String {
179    #[inline]
180    fn from(ch: char) -> Self {
181        ch.to_string().into()
182    }
183}
184
185impl From<Cow<'_, str>> for String {
186    #[inline]
187    fn from(moo: Cow<'_, str>) -> Self {
188        moo.into_owned().into()
189    }
190}
191
192impl From<Vec<u8>> for String {
193    #[inline]
194    fn from(vec: Vec<u8>) -> Self {
195        Self::from_bytes(vec)
196    }
197}
198
199impl From<PathBuf> for String {
200    #[inline]
201    fn from(path: PathBuf) -> Self {
202        path.display().to_string().into()
203    }
204}
205
206#[cfg(not(windows))]
207impl From<String> for PathBuf {
208    #[inline]
209    fn from(nstr: String) -> Self {
210        use std::os::unix::ffi::OsStrExt;
211        OsStr::from_bytes(nstr.as_bytes()).to_owned().into()
212    }
213}
214
215#[cfg(windows)]
216impl From<String> for PathBuf {
217    #[inline]
218    fn from(nstr: String) -> Self {
219        StdString::from_utf8_lossy(nstr.as_bytes()).into_owned().into()
220    }
221}
222
223impl PartialEq<Self> for String {
224    #[inline]
225    fn eq(&self, other: &Self) -> bool {
226        self.as_bytes() == other.as_bytes()
227    }
228}
229
230impl PartialEq<str> for String {
231    #[inline]
232    fn eq(&self, other: &str) -> bool {
233        self.as_bytes() == other.as_bytes()
234    }
235}
236
237impl PartialEq<&str> for String {
238    #[inline]
239    fn eq(&self, other: &&str) -> bool {
240        self.as_bytes() == other.as_bytes()
241    }
242}
243
244impl PartialEq<StdString> for String {
245    #[inline]
246    fn eq(&self, other: &StdString) -> bool {
247        self.as_bytes() == other.as_bytes()
248    }
249}
250
251impl TryFrom<String> for StdString {
252    type Error = std::string::FromUtf8Error;
253
254    fn try_from(s: String) -> Result<Self, Self::Error> {
255        StdString::from_utf8(s.into_bytes())
256    }
257}
258
259impl Pushable for String {
260    unsafe fn push(self, lstate: *mut lua_State) -> Result<c_int, lua::Error> {
261        lua::ffi::lua_pushlstring(lstate, self.as_ptr(), self.len());
262        Ok(1)
263    }
264}
265
266impl Poppable for String {
267    unsafe fn pop(lstate: *mut lua_State) -> Result<Self, lua::Error> {
268        <StdString as Poppable>::pop(lstate).map(Into::into)
269    }
270}
271
272#[cfg(feature = "serde")]
273mod serde {
274    use std::fmt;
275
276    use serde::de::{self, Deserialize, Deserializer, Visitor};
277
278    impl<'de> Deserialize<'de> for super::String {
279        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
280        where
281            D: Deserializer<'de>,
282        {
283            struct StringVisitor;
284
285            impl<'de> Visitor<'de> for StringVisitor {
286                type Value = crate::String;
287
288                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
289                    f.write_str("either a string of a byte vector")
290                }
291
292                fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
293                where
294                    E: de::Error,
295                {
296                    Ok(crate::String::from_bytes(b.to_owned()))
297                }
298
299                fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
300                where
301                    E: de::Error,
302                {
303                    Ok(crate::String::from(s))
304                }
305            }
306
307            deserializer.deserialize_str(StringVisitor)
308        }
309    }
310}
311
312#[cfg(test)]
313mod tests {
314    use super::*;
315
316    #[test]
317    fn partial_eq() {
318        let lhs = String::from("foo bar baz");
319        let rhs = String::from("foo bar baz");
320        assert_eq!(lhs, rhs);
321
322        let lhs = String::from("foo bar baz");
323        let rhs = String::from("bar foo baz");
324        assert_ne!(lhs, rhs);
325
326        let lhs = String::from("€");
327        let rhs = "€";
328        assert_eq!(lhs, rhs);
329    }
330
331    #[test]
332    fn clone() {
333        let lhs = String::from("abc");
334        let rhs = lhs.clone();
335
336        assert_eq!(lhs, rhs);
337    }
338
339    #[test]
340    fn from_string() {
341        let foo = StdString::from("foo bar baz");
342
343        let lhs = String::from(foo.as_ref());
344        let rhs = String::from(foo);
345
346        assert_eq!(lhs, rhs);
347    }
348
349    #[test]
350    fn to_bytes() {
351        let s = String::from("hello");
352        let bytes = s.into_bytes();
353        assert_eq!(&[104, 101, 108, 108, 111][..], &bytes[..]);
354    }
355}