1use alloc::borrow::Cow;
4use alloc::string::String as StdString;
5use core::str::{self, Utf8Error};
6use core::{ffi, fmt};
7use std::path::{Path, PathBuf};
8
9use luajit as lua;
10
11use crate::{NvimStr, Object, ObjectKind, StringBuilder, conversion};
12
13#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
20#[repr(C)]
21pub struct String {
22 inner: NvimStr<'static>,
23}
24
25impl String {
26 #[inline]
27 pub fn as_bytes(&self) -> &[u8] {
28 self.inner.as_bytes()
29 }
30
31 #[inline]
33 pub fn as_ptr(&self) -> *const ffi::c_char {
34 self.inner.as_ptr()
35 }
36
37 #[inline]
39 pub fn as_nvim_str(&self) -> NvimStr<'_> {
40 self.inner.reborrow()
41 }
42
43 #[inline]
47 pub fn from_bytes(bytes: &[u8]) -> Self {
48 let mut s = StringBuilder::with_capacity(bytes.len());
49 s.push_bytes(bytes);
50 s.finish()
51 }
52
53 #[inline]
60 pub unsafe fn from_raw_parts(
61 data: *const ffi::c_char,
62 len: usize,
63 ) -> Self {
64 Self { inner: NvimStr::from_raw_parts(data, len) }
65 }
66
67 #[inline]
69 pub fn is_empty(&self) -> bool {
70 self.len() == 0
71 }
72
73 #[inline]
76 pub fn len(&self) -> usize {
77 self.inner.len()
78 }
79
80 #[inline]
82 pub fn new() -> Self {
83 Self::from_bytes(&[])
84 }
85
86 #[inline]
92 pub unsafe fn set_len(&mut self, new_len: usize) {
93 self.inner.set_len(new_len);
94 }
95
96 #[inline]
98 pub fn to_str(&self) -> Result<&str, Utf8Error> {
99 self.inner.to_str()
100 }
101
102 #[inline]
106 pub fn to_string_lossy(&self) -> Cow<'_, str> {
107 self.inner.to_string_lossy()
108 }
109}
110
111impl Default for String {
112 #[inline]
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118impl fmt::Debug for String {
119 #[inline]
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 fmt::Debug::fmt(&self.inner, f)
122 }
123}
124
125impl fmt::Display for String {
126 #[inline]
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 fmt::Display::fmt(&self.inner, f)
129 }
130}
131
132impl Clone for String {
133 #[inline]
134 fn clone(&self) -> Self {
135 Self::from_bytes(self.as_bytes())
136 }
137}
138
139impl Drop for String {
140 fn drop(&mut self) {
141 }
147}
148
149impl From<&str> for String {
150 #[inline]
151 fn from(s: &str) -> Self {
152 Self::from_bytes(s.as_bytes())
153 }
154}
155
156impl From<StdString> for String {
157 #[inline]
158 fn from(s: StdString) -> Self {
159 s.as_str().into()
160 }
161}
162
163impl From<char> for String {
164 #[inline]
165 fn from(ch: char) -> Self {
166 ch.to_string().into()
167 }
168}
169
170impl From<Cow<'_, str>> for String {
171 #[inline]
172 fn from(moo: Cow<'_, str>) -> Self {
173 moo.as_ref().into()
174 }
175}
176
177impl From<&Path> for String {
178 #[inline]
179 fn from(path: &Path) -> Self {
180 path.display().to_string().into()
181 }
182}
183
184#[cfg(not(windows))]
185impl From<String> for PathBuf {
186 #[inline]
187 fn from(nstr: String) -> Self {
188 use std::os::unix::ffi::OsStrExt;
189 std::ffi::OsStr::from_bytes(nstr.as_bytes()).to_owned().into()
190 }
191}
192
193#[cfg(windows)]
194impl From<String> for PathBuf {
195 #[inline]
196 fn from(nstr: String) -> Self {
197 StdString::from_utf8_lossy(nstr.as_bytes()).into_owned().into()
198 }
199}
200
201impl PartialEq<str> for String {
202 #[inline]
203 fn eq(&self, other: &str) -> bool {
204 self.as_bytes() == other.as_bytes()
205 }
206}
207
208impl PartialEq<String> for str {
209 #[inline]
210 fn eq(&self, other: &String) -> bool {
211 other == self
212 }
213}
214
215impl PartialEq<&str> for String {
216 #[inline]
217 fn eq(&self, other: &&str) -> bool {
218 self.as_bytes() == other.as_bytes()
219 }
220}
221
222impl PartialEq<String> for &str {
223 #[inline]
224 fn eq(&self, other: &String) -> bool {
225 other == self
226 }
227}
228
229impl PartialEq<std::string::String> for String {
230 #[inline]
231 fn eq(&self, other: &std::string::String) -> bool {
232 self.as_bytes() == other.as_bytes()
233 }
234}
235
236impl TryFrom<Object> for String {
237 type Error = conversion::Error;
238
239 #[inline]
240 fn try_from(obj: Object) -> Result<Self, Self::Error> {
241 match obj.kind() {
242 ObjectKind::String => Ok(unsafe { obj.into_string_unchecked() }),
243 other => Err(conversion::Error::FromWrongType {
244 expected: "string",
245 actual: other.as_static(),
246 }),
247 }
248 }
249}
250
251impl lua::Pushable for String {
252 #[inline]
253 unsafe fn push(
254 self,
255 lstate: *mut lua::ffi::State,
256 ) -> Result<ffi::c_int, lua::Error> {
257 lua::ffi::lua_pushlstring(lstate, self.as_ptr(), self.len());
258 Ok(1)
259 }
260}
261
262impl lua::Poppable for String {
263 #[inline]
264 unsafe fn pop(lstate: *mut lua::ffi::State) -> Result<Self, lua::Error> {
265 use lua::ffi::*;
266
267 if lua_gettop(lstate) < 0 {
268 return Err(lua::Error::PopEmptyStack);
269 }
270
271 let ty = lua_type(lstate, -1);
272
273 if ty != LUA_TSTRING && ty != LUA_TNUMBER {
274 return Err(lua::Error::pop_wrong_type::<Self>(LUA_TSTRING, ty));
275 }
276
277 let mut len = 0;
278 let ptr = lua_tolstring(lstate, -1, &mut len);
279
280 assert!(!ptr.is_null());
283
284 let slice = std::slice::from_raw_parts(ptr as *const u8, len);
285 let s = String::from_bytes(slice);
286
287 lua_pop(lstate, 1);
288
289 Ok(s)
290 }
291}
292
293#[cfg(feature = "serde")]
294mod serde {
295 use std::fmt;
296
297 use serde::de::{self, Deserialize, Deserializer, Visitor};
298
299 use super::String as NvimString;
300
301 impl<'de> Deserialize<'de> for super::String {
302 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303 where
304 D: Deserializer<'de>,
305 {
306 struct StringVisitor;
307
308 impl Visitor<'_> for StringVisitor {
309 type Value = NvimString;
310
311 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 f.write_str("either a string of a byte vector")
313 }
314
315 fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
316 where
317 E: de::Error,
318 {
319 Ok(NvimString::from_bytes(b))
320 }
321
322 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
323 where
324 E: de::Error,
325 {
326 Ok(NvimString::from(s))
327 }
328 }
329
330 deserializer.deserialize_str(StringVisitor)
331 }
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn as_bytes() {
341 let s = String::from("hello");
342 assert_eq!(s.as_bytes(), b"hello");
343 }
344
345 #[test]
346 fn empty_from_bytes() {
347 let s = String::from_bytes(b"");
348 assert_eq!(s.len(), 0);
349 assert!(!s.as_ptr().is_null());
350 }
351
352 #[test]
353 fn from_bytes() {
354 let s = String::from_bytes(b"Hello World!");
355 assert_eq!(s.len(), 12);
356 }
357
358 #[test]
359 fn partial_eq() {
360 let lhs = String::from("foo bar baz");
361 let rhs = String::from("foo bar baz");
362 assert_eq!(lhs, rhs);
363
364 let lhs = String::from("foo bar baz");
365 let rhs = StdString::from("bar foo baz");
366 assert_ne!(lhs, rhs);
367
368 let lhs = String::from("€");
369 let rhs = "€";
370 assert_eq!(lhs, rhs);
371 }
372
373 #[test]
374 fn clone() {
375 let lhs = String::from("abc");
376 let rhs = lhs.clone();
377
378 assert_eq!(lhs, rhs);
379 }
380
381 #[test]
382 fn from_string() {
383 let foo = StdString::from("foo bar baz");
384
385 let lhs = String::from(foo.as_str());
386 let rhs = String::from(foo.as_str());
387
388 assert_eq!(lhs, rhs);
389 }
390}