1 | //! Convenient and efficient string argument passing. |
2 | //! |
3 | //! This module defines the `Arg` trait and implements it for several common |
4 | //! string types. This allows users to pass any of these string types directly |
5 | //! to rustix APIs with string arguments, and it allows rustix to implement |
6 | //! NUL-termination without the need for copying where possible. |
7 | |
8 | use crate::ffi::CStr; |
9 | use crate::io; |
10 | #[cfg (feature = "itoa" )] |
11 | use crate::path::DecInt; |
12 | use crate::path::SMALL_PATH_BUFFER_SIZE; |
13 | #[cfg (all(feature = "alloc" , feature = "itoa" ))] |
14 | use alloc::borrow::ToOwned; |
15 | use core::mem::MaybeUninit; |
16 | use core::{ptr, slice, str}; |
17 | #[cfg (feature = "std" )] |
18 | use std::ffi::{OsStr, OsString}; |
19 | #[cfg (all(feature = "std" , target_os = "hermit" ))] |
20 | use std::os::hermit::ext::ffi::{OsStrExt, OsStringExt}; |
21 | #[cfg (all(feature = "std" , unix))] |
22 | use std::os::unix::ffi::{OsStrExt, OsStringExt}; |
23 | #[cfg (all(feature = "std" , target_os = "vxworks" ))] |
24 | use std::os::vxworks::ext::ffi::{OsStrExt, OsStringExt}; |
25 | #[cfg (all(feature = "std" , target_os = "wasi" ))] |
26 | use std::os::wasi::ffi::{OsStrExt, OsStringExt}; |
27 | #[cfg (feature = "std" )] |
28 | use std::path::{Component, Components, Iter, Path, PathBuf}; |
29 | #[cfg (feature = "alloc" )] |
30 | use {crate::ffi::CString, alloc::borrow::Cow}; |
31 | #[cfg (feature = "alloc" )] |
32 | use {alloc::string::String, alloc::vec::Vec}; |
33 | |
34 | /// A trait for passing path arguments. |
35 | /// |
36 | /// This is similar to [`AsRef`]`<`[`Path`]`>`, but is implemented for more |
37 | /// kinds of strings and can convert into more kinds of strings. |
38 | /// |
39 | /// # Example |
40 | /// |
41 | /// ``` |
42 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
43 | /// use rustix::ffi::CStr; |
44 | /// use rustix::io; |
45 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
46 | /// use rustix::path::Arg; |
47 | /// |
48 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
49 | /// pub fn touch<P: Arg>(path: P) -> io::Result<()> { |
50 | /// let path = path.into_c_str()?; |
51 | /// _touch(&path) |
52 | /// } |
53 | /// |
54 | /// # #[cfg (any(feature = "fs" , feature = "net" ))] |
55 | /// fn _touch(path: &CStr) -> io::Result<()> { |
56 | /// // implementation goes here |
57 | /// Ok(()) |
58 | /// } |
59 | /// ``` |
60 | /// |
61 | /// Users can then call `touch("foo")`, `touch(cstr!("foo"))`, |
62 | /// `touch(Path::new("foo"))`, or many other things. |
63 | /// |
64 | /// [`AsRef`]: std::convert::AsRef |
65 | pub trait Arg { |
66 | /// Returns a view of this string as a string slice. |
67 | fn as_str(&self) -> io::Result<&str>; |
68 | |
69 | /// Returns a potentially-lossy rendering of this string as a |
70 | /// `Cow<'_, str>`. |
71 | #[cfg (feature = "alloc" )] |
72 | fn to_string_lossy(&self) -> Cow<'_, str>; |
73 | |
74 | /// Returns a view of this string as a maybe-owned [`CStr`]. |
75 | #[cfg (feature = "alloc" )] |
76 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>>; |
77 | |
78 | /// Consumes `self` and returns a view of this string as a maybe-owned |
79 | /// [`CStr`]. |
80 | #[cfg (feature = "alloc" )] |
81 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
82 | where |
83 | Self: 'b; |
84 | |
85 | /// Runs a closure with `self` passed in as a `&CStr`. |
86 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
87 | where |
88 | Self: Sized, |
89 | F: FnOnce(&CStr) -> io::Result<T>; |
90 | } |
91 | |
92 | impl Arg for &str { |
93 | #[inline ] |
94 | fn as_str(&self) -> io::Result<&str> { |
95 | Ok(self) |
96 | } |
97 | |
98 | #[cfg (feature = "alloc" )] |
99 | #[inline ] |
100 | fn to_string_lossy(&self) -> Cow<'_, str> { |
101 | Cow::Borrowed(self) |
102 | } |
103 | |
104 | #[cfg (feature = "alloc" )] |
105 | #[inline ] |
106 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
107 | Ok(Cow::Owned( |
108 | CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
109 | )) |
110 | } |
111 | |
112 | #[cfg (feature = "alloc" )] |
113 | #[inline ] |
114 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
115 | where |
116 | Self: 'b, |
117 | { |
118 | Ok(Cow::Owned( |
119 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
120 | )) |
121 | } |
122 | |
123 | #[inline ] |
124 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
125 | where |
126 | Self: Sized, |
127 | F: FnOnce(&CStr) -> io::Result<T>, |
128 | { |
129 | with_c_str(self.as_bytes(), f) |
130 | } |
131 | } |
132 | |
133 | #[cfg (feature = "alloc" )] |
134 | impl Arg for &String { |
135 | #[inline ] |
136 | fn as_str(&self) -> io::Result<&str> { |
137 | Ok(self) |
138 | } |
139 | |
140 | #[cfg (feature = "alloc" )] |
141 | #[inline ] |
142 | fn to_string_lossy(&self) -> Cow<'_, str> { |
143 | Cow::Borrowed(self) |
144 | } |
145 | |
146 | #[cfg (feature = "alloc" )] |
147 | #[inline ] |
148 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
149 | Ok(Cow::Owned( |
150 | CString::new(String::as_str(self)).map_err(|_cstr_err| io::Errno::INVAL)?, |
151 | )) |
152 | } |
153 | |
154 | #[cfg (feature = "alloc" )] |
155 | #[inline ] |
156 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
157 | where |
158 | Self: 'b, |
159 | { |
160 | self.as_str().into_c_str() |
161 | } |
162 | |
163 | #[inline ] |
164 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
165 | where |
166 | Self: Sized, |
167 | F: FnOnce(&CStr) -> io::Result<T>, |
168 | { |
169 | with_c_str(self.as_bytes(), f) |
170 | } |
171 | } |
172 | |
173 | #[cfg (feature = "alloc" )] |
174 | impl Arg for String { |
175 | #[inline ] |
176 | fn as_str(&self) -> io::Result<&str> { |
177 | Ok(self) |
178 | } |
179 | |
180 | #[cfg (feature = "alloc" )] |
181 | #[inline ] |
182 | fn to_string_lossy(&self) -> Cow<'_, str> { |
183 | Cow::Borrowed(self) |
184 | } |
185 | |
186 | #[cfg (feature = "alloc" )] |
187 | #[inline ] |
188 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
189 | Ok(Cow::Owned( |
190 | CString::new(self.as_str()).map_err(|_cstr_err| io::Errno::INVAL)?, |
191 | )) |
192 | } |
193 | |
194 | #[cfg (feature = "alloc" )] |
195 | #[inline ] |
196 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
197 | where |
198 | Self: 'b, |
199 | { |
200 | Ok(Cow::Owned( |
201 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
202 | )) |
203 | } |
204 | |
205 | #[inline ] |
206 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
207 | where |
208 | Self: Sized, |
209 | F: FnOnce(&CStr) -> io::Result<T>, |
210 | { |
211 | f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
212 | } |
213 | } |
214 | |
215 | #[cfg (feature = "std" )] |
216 | impl Arg for &OsStr { |
217 | #[inline ] |
218 | fn as_str(&self) -> io::Result<&str> { |
219 | self.to_str().ok_or(io::Errno::INVAL) |
220 | } |
221 | |
222 | #[cfg (feature = "alloc" )] |
223 | #[inline ] |
224 | fn to_string_lossy(&self) -> Cow<'_, str> { |
225 | OsStr::to_string_lossy(self) |
226 | } |
227 | |
228 | #[cfg (feature = "alloc" )] |
229 | #[inline ] |
230 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
231 | Ok(Cow::Owned( |
232 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
233 | )) |
234 | } |
235 | |
236 | #[cfg (feature = "alloc" )] |
237 | #[inline ] |
238 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
239 | where |
240 | Self: 'b, |
241 | { |
242 | Ok(Cow::Owned( |
243 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
244 | )) |
245 | } |
246 | |
247 | #[inline ] |
248 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
249 | where |
250 | Self: Sized, |
251 | F: FnOnce(&CStr) -> io::Result<T>, |
252 | { |
253 | with_c_str(self.as_bytes(), f) |
254 | } |
255 | } |
256 | |
257 | #[cfg (feature = "std" )] |
258 | impl Arg for &OsString { |
259 | #[inline ] |
260 | fn as_str(&self) -> io::Result<&str> { |
261 | OsString::as_os_str(self).to_str().ok_or(io::Errno::INVAL) |
262 | } |
263 | |
264 | #[cfg (feature = "alloc" )] |
265 | #[inline ] |
266 | fn to_string_lossy(&self) -> Cow<'_, str> { |
267 | self.as_os_str().to_string_lossy() |
268 | } |
269 | |
270 | #[cfg (feature = "alloc" )] |
271 | #[inline ] |
272 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
273 | Ok(Cow::Owned( |
274 | CString::new(OsString::as_os_str(self).as_bytes()) |
275 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
276 | )) |
277 | } |
278 | |
279 | #[cfg (feature = "alloc" )] |
280 | #[inline ] |
281 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
282 | where |
283 | Self: 'b, |
284 | { |
285 | self.as_os_str().into_c_str() |
286 | } |
287 | |
288 | #[inline ] |
289 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
290 | where |
291 | Self: Sized, |
292 | F: FnOnce(&CStr) -> io::Result<T>, |
293 | { |
294 | with_c_str(self.as_bytes(), f) |
295 | } |
296 | } |
297 | |
298 | #[cfg (feature = "std" )] |
299 | impl Arg for OsString { |
300 | #[inline ] |
301 | fn as_str(&self) -> io::Result<&str> { |
302 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
303 | } |
304 | |
305 | #[cfg (feature = "alloc" )] |
306 | #[inline ] |
307 | fn to_string_lossy(&self) -> Cow<'_, str> { |
308 | self.as_os_str().to_string_lossy() |
309 | } |
310 | |
311 | #[cfg (feature = "alloc" )] |
312 | #[inline ] |
313 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
314 | Ok(Cow::Owned( |
315 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
316 | )) |
317 | } |
318 | |
319 | #[cfg (feature = "alloc" )] |
320 | #[inline ] |
321 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
322 | where |
323 | Self: 'b, |
324 | { |
325 | Ok(Cow::Owned( |
326 | CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
327 | )) |
328 | } |
329 | |
330 | #[inline ] |
331 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
332 | where |
333 | Self: Sized, |
334 | F: FnOnce(&CStr) -> io::Result<T>, |
335 | { |
336 | f(&CString::new(self.into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?) |
337 | } |
338 | } |
339 | |
340 | #[cfg (feature = "std" )] |
341 | impl Arg for &Path { |
342 | #[inline ] |
343 | fn as_str(&self) -> io::Result<&str> { |
344 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
345 | } |
346 | |
347 | #[cfg (feature = "alloc" )] |
348 | #[inline ] |
349 | fn to_string_lossy(&self) -> Cow<'_, str> { |
350 | Path::to_string_lossy(self) |
351 | } |
352 | |
353 | #[cfg (feature = "alloc" )] |
354 | #[inline ] |
355 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
356 | Ok(Cow::Owned( |
357 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
358 | )) |
359 | } |
360 | |
361 | #[cfg (feature = "alloc" )] |
362 | #[inline ] |
363 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
364 | where |
365 | Self: 'b, |
366 | { |
367 | Ok(Cow::Owned( |
368 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
369 | )) |
370 | } |
371 | |
372 | #[inline ] |
373 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
374 | where |
375 | Self: Sized, |
376 | F: FnOnce(&CStr) -> io::Result<T>, |
377 | { |
378 | with_c_str(self.as_os_str().as_bytes(), f) |
379 | } |
380 | } |
381 | |
382 | #[cfg (feature = "std" )] |
383 | impl Arg for &PathBuf { |
384 | #[inline ] |
385 | fn as_str(&self) -> io::Result<&str> { |
386 | PathBuf::as_path(self) |
387 | .as_os_str() |
388 | .to_str() |
389 | .ok_or(io::Errno::INVAL) |
390 | } |
391 | |
392 | #[cfg (feature = "alloc" )] |
393 | #[inline ] |
394 | fn to_string_lossy(&self) -> Cow<'_, str> { |
395 | self.as_path().to_string_lossy() |
396 | } |
397 | |
398 | #[cfg (feature = "alloc" )] |
399 | #[inline ] |
400 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
401 | Ok(Cow::Owned( |
402 | CString::new(PathBuf::as_path(self).as_os_str().as_bytes()) |
403 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
404 | )) |
405 | } |
406 | |
407 | #[cfg (feature = "alloc" )] |
408 | #[inline ] |
409 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
410 | where |
411 | Self: 'b, |
412 | { |
413 | self.as_path().into_c_str() |
414 | } |
415 | |
416 | #[inline ] |
417 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
418 | where |
419 | Self: Sized, |
420 | F: FnOnce(&CStr) -> io::Result<T>, |
421 | { |
422 | with_c_str(self.as_os_str().as_bytes(), f) |
423 | } |
424 | } |
425 | |
426 | #[cfg (feature = "std" )] |
427 | impl Arg for PathBuf { |
428 | #[inline ] |
429 | fn as_str(&self) -> io::Result<&str> { |
430 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
431 | } |
432 | |
433 | #[cfg (feature = "alloc" )] |
434 | #[inline ] |
435 | fn to_string_lossy(&self) -> Cow<'_, str> { |
436 | self.as_os_str().to_string_lossy() |
437 | } |
438 | |
439 | #[cfg (feature = "alloc" )] |
440 | #[inline ] |
441 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
442 | Ok(Cow::Owned( |
443 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
444 | )) |
445 | } |
446 | |
447 | #[cfg (feature = "alloc" )] |
448 | #[inline ] |
449 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
450 | where |
451 | Self: 'b, |
452 | { |
453 | Ok(Cow::Owned( |
454 | CString::new(self.into_os_string().into_vec()).map_err(|_cstr_err| io::Errno::INVAL)?, |
455 | )) |
456 | } |
457 | |
458 | #[inline ] |
459 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
460 | where |
461 | Self: Sized, |
462 | F: FnOnce(&CStr) -> io::Result<T>, |
463 | { |
464 | f( |
465 | &CString::new(self.into_os_string().into_vec()) |
466 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
467 | ) |
468 | } |
469 | } |
470 | |
471 | impl Arg for &CStr { |
472 | #[inline ] |
473 | fn as_str(&self) -> io::Result<&str> { |
474 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
475 | } |
476 | |
477 | #[cfg (feature = "alloc" )] |
478 | #[inline ] |
479 | fn to_string_lossy(&self) -> Cow<'_, str> { |
480 | CStr::to_string_lossy(self) |
481 | } |
482 | |
483 | #[cfg (feature = "alloc" )] |
484 | #[inline ] |
485 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
486 | Ok(Cow::Borrowed(self)) |
487 | } |
488 | |
489 | #[cfg (feature = "alloc" )] |
490 | #[inline ] |
491 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
492 | where |
493 | Self: 'b, |
494 | { |
495 | Ok(Cow::Borrowed(self)) |
496 | } |
497 | |
498 | #[inline ] |
499 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
500 | where |
501 | Self: Sized, |
502 | F: FnOnce(&CStr) -> io::Result<T>, |
503 | { |
504 | f(self) |
505 | } |
506 | } |
507 | |
508 | #[cfg (feature = "alloc" )] |
509 | impl Arg for &CString { |
510 | #[inline ] |
511 | fn as_str(&self) -> io::Result<&str> { |
512 | unimplemented!() |
513 | } |
514 | |
515 | #[cfg (feature = "alloc" )] |
516 | #[inline ] |
517 | fn to_string_lossy(&self) -> Cow<'_, str> { |
518 | unimplemented!() |
519 | } |
520 | |
521 | #[cfg (feature = "alloc" )] |
522 | #[inline ] |
523 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
524 | Ok(Cow::Borrowed(self)) |
525 | } |
526 | |
527 | #[cfg (feature = "alloc" )] |
528 | #[inline ] |
529 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
530 | where |
531 | Self: 'b, |
532 | { |
533 | Ok(Cow::Borrowed(self)) |
534 | } |
535 | |
536 | #[inline ] |
537 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
538 | where |
539 | Self: Sized, |
540 | F: FnOnce(&CStr) -> io::Result<T>, |
541 | { |
542 | f(self) |
543 | } |
544 | } |
545 | |
546 | #[cfg (feature = "alloc" )] |
547 | impl Arg for CString { |
548 | #[inline ] |
549 | fn as_str(&self) -> io::Result<&str> { |
550 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
551 | } |
552 | |
553 | #[cfg (feature = "alloc" )] |
554 | #[inline ] |
555 | fn to_string_lossy(&self) -> Cow<'_, str> { |
556 | CStr::to_string_lossy(self) |
557 | } |
558 | |
559 | #[cfg (feature = "alloc" )] |
560 | #[inline ] |
561 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
562 | Ok(Cow::Borrowed(self)) |
563 | } |
564 | |
565 | #[cfg (feature = "alloc" )] |
566 | #[inline ] |
567 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
568 | where |
569 | Self: 'b, |
570 | { |
571 | Ok(Cow::Owned(self)) |
572 | } |
573 | |
574 | #[inline ] |
575 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
576 | where |
577 | Self: Sized, |
578 | F: FnOnce(&CStr) -> io::Result<T>, |
579 | { |
580 | f(&self) |
581 | } |
582 | } |
583 | |
584 | #[cfg (feature = "alloc" )] |
585 | impl<'a> Arg for Cow<'a, str> { |
586 | #[inline ] |
587 | fn as_str(&self) -> io::Result<&str> { |
588 | Ok(self) |
589 | } |
590 | |
591 | #[cfg (feature = "alloc" )] |
592 | #[inline ] |
593 | fn to_string_lossy(&self) -> Cow<'_, str> { |
594 | Cow::Borrowed(self) |
595 | } |
596 | |
597 | #[cfg (feature = "alloc" )] |
598 | #[inline ] |
599 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
600 | Ok(Cow::Owned( |
601 | CString::new(self.as_ref()).map_err(|_cstr_err| io::Errno::INVAL)?, |
602 | )) |
603 | } |
604 | |
605 | #[cfg (feature = "alloc" )] |
606 | #[inline ] |
607 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
608 | where |
609 | Self: 'b, |
610 | { |
611 | Ok(Cow::Owned( |
612 | match self { |
613 | Cow::Owned(s) => CString::new(s), |
614 | Cow::Borrowed(s) => CString::new(s), |
615 | } |
616 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
617 | )) |
618 | } |
619 | |
620 | #[inline ] |
621 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
622 | where |
623 | Self: Sized, |
624 | F: FnOnce(&CStr) -> io::Result<T>, |
625 | { |
626 | with_c_str(self.as_bytes(), f) |
627 | } |
628 | } |
629 | |
630 | #[cfg (feature = "std" )] |
631 | #[cfg (feature = "alloc" )] |
632 | impl<'a> Arg for Cow<'a, OsStr> { |
633 | #[inline ] |
634 | fn as_str(&self) -> io::Result<&str> { |
635 | (**self).to_str().ok_or(io::Errno::INVAL) |
636 | } |
637 | |
638 | #[cfg (feature = "alloc" )] |
639 | #[inline ] |
640 | fn to_string_lossy(&self) -> Cow<'_, str> { |
641 | (**self).to_string_lossy() |
642 | } |
643 | |
644 | #[cfg (feature = "alloc" )] |
645 | #[inline ] |
646 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
647 | Ok(Cow::Owned( |
648 | CString::new(self.as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
649 | )) |
650 | } |
651 | |
652 | #[cfg (feature = "alloc" )] |
653 | #[inline ] |
654 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
655 | where |
656 | Self: 'b, |
657 | { |
658 | Ok(Cow::Owned( |
659 | match self { |
660 | Cow::Owned(os) => CString::new(os.into_vec()), |
661 | Cow::Borrowed(os) => CString::new(os.as_bytes()), |
662 | } |
663 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
664 | )) |
665 | } |
666 | |
667 | #[inline ] |
668 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
669 | where |
670 | Self: Sized, |
671 | F: FnOnce(&CStr) -> io::Result<T>, |
672 | { |
673 | with_c_str(self.as_bytes(), f) |
674 | } |
675 | } |
676 | |
677 | #[cfg (feature = "alloc" )] |
678 | impl<'a> Arg for Cow<'a, CStr> { |
679 | #[inline ] |
680 | fn as_str(&self) -> io::Result<&str> { |
681 | self.to_str().map_err(|_utf8_err| io::Errno::INVAL) |
682 | } |
683 | |
684 | #[cfg (feature = "alloc" )] |
685 | #[inline ] |
686 | fn to_string_lossy(&self) -> Cow<'_, str> { |
687 | let borrow: &CStr = core::borrow::Borrow::borrow(self); |
688 | borrow.to_string_lossy() |
689 | } |
690 | |
691 | #[cfg (feature = "alloc" )] |
692 | #[inline ] |
693 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
694 | Ok(Cow::Borrowed(self)) |
695 | } |
696 | |
697 | #[cfg (feature = "alloc" )] |
698 | #[inline ] |
699 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
700 | where |
701 | Self: 'b, |
702 | { |
703 | Ok(self) |
704 | } |
705 | |
706 | #[inline ] |
707 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
708 | where |
709 | Self: Sized, |
710 | F: FnOnce(&CStr) -> io::Result<T>, |
711 | { |
712 | f(&self) |
713 | } |
714 | } |
715 | |
716 | #[cfg (feature = "std" )] |
717 | impl<'a> Arg for Component<'a> { |
718 | #[inline ] |
719 | fn as_str(&self) -> io::Result<&str> { |
720 | self.as_os_str().to_str().ok_or(io::Errno::INVAL) |
721 | } |
722 | |
723 | #[cfg (feature = "alloc" )] |
724 | #[inline ] |
725 | fn to_string_lossy(&self) -> Cow<'_, str> { |
726 | self.as_os_str().to_string_lossy() |
727 | } |
728 | |
729 | #[cfg (feature = "alloc" )] |
730 | #[inline ] |
731 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
732 | Ok(Cow::Owned( |
733 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
734 | )) |
735 | } |
736 | |
737 | #[cfg (feature = "alloc" )] |
738 | #[inline ] |
739 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
740 | where |
741 | Self: 'b, |
742 | { |
743 | Ok(Cow::Owned( |
744 | CString::new(self.as_os_str().as_bytes()).map_err(|_cstr_err| io::Errno::INVAL)?, |
745 | )) |
746 | } |
747 | |
748 | #[inline ] |
749 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
750 | where |
751 | Self: Sized, |
752 | F: FnOnce(&CStr) -> io::Result<T>, |
753 | { |
754 | with_c_str(self.as_os_str().as_bytes(), f) |
755 | } |
756 | } |
757 | |
758 | #[cfg (feature = "std" )] |
759 | impl<'a> Arg for Components<'a> { |
760 | #[inline ] |
761 | fn as_str(&self) -> io::Result<&str> { |
762 | self.as_path().to_str().ok_or(io::Errno::INVAL) |
763 | } |
764 | |
765 | #[cfg (feature = "alloc" )] |
766 | #[inline ] |
767 | fn to_string_lossy(&self) -> Cow<'_, str> { |
768 | self.as_path().to_string_lossy() |
769 | } |
770 | |
771 | #[cfg (feature = "alloc" )] |
772 | #[inline ] |
773 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
774 | Ok(Cow::Owned( |
775 | CString::new(self.as_path().as_os_str().as_bytes()) |
776 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
777 | )) |
778 | } |
779 | |
780 | #[cfg (feature = "alloc" )] |
781 | #[inline ] |
782 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
783 | where |
784 | Self: 'b, |
785 | { |
786 | Ok(Cow::Owned( |
787 | CString::new(self.as_path().as_os_str().as_bytes()) |
788 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
789 | )) |
790 | } |
791 | |
792 | #[inline ] |
793 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
794 | where |
795 | Self: Sized, |
796 | F: FnOnce(&CStr) -> io::Result<T>, |
797 | { |
798 | with_c_str(self.as_path().as_os_str().as_bytes(), f) |
799 | } |
800 | } |
801 | |
802 | #[cfg (feature = "std" )] |
803 | impl<'a> Arg for Iter<'a> { |
804 | #[inline ] |
805 | fn as_str(&self) -> io::Result<&str> { |
806 | self.as_path().to_str().ok_or(io::Errno::INVAL) |
807 | } |
808 | |
809 | #[cfg (feature = "alloc" )] |
810 | #[inline ] |
811 | fn to_string_lossy(&self) -> Cow<'_, str> { |
812 | self.as_path().to_string_lossy() |
813 | } |
814 | |
815 | #[cfg (feature = "alloc" )] |
816 | #[inline ] |
817 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
818 | Ok(Cow::Owned( |
819 | CString::new(self.as_path().as_os_str().as_bytes()) |
820 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
821 | )) |
822 | } |
823 | |
824 | #[cfg (feature = "alloc" )] |
825 | #[inline ] |
826 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
827 | where |
828 | Self: 'b, |
829 | { |
830 | Ok(Cow::Owned( |
831 | CString::new(self.as_path().as_os_str().as_bytes()) |
832 | .map_err(|_cstr_err| io::Errno::INVAL)?, |
833 | )) |
834 | } |
835 | |
836 | #[inline ] |
837 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
838 | where |
839 | Self: Sized, |
840 | F: FnOnce(&CStr) -> io::Result<T>, |
841 | { |
842 | with_c_str(self.as_path().as_os_str().as_bytes(), f) |
843 | } |
844 | } |
845 | |
846 | impl Arg for &[u8] { |
847 | #[inline ] |
848 | fn as_str(&self) -> io::Result<&str> { |
849 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
850 | } |
851 | |
852 | #[cfg (feature = "alloc" )] |
853 | #[inline ] |
854 | fn to_string_lossy(&self) -> Cow<'_, str> { |
855 | String::from_utf8_lossy(self) |
856 | } |
857 | |
858 | #[cfg (feature = "alloc" )] |
859 | #[inline ] |
860 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
861 | Ok(Cow::Owned( |
862 | CString::new(*self).map_err(|_cstr_err| io::Errno::INVAL)?, |
863 | )) |
864 | } |
865 | |
866 | #[cfg (feature = "alloc" )] |
867 | #[inline ] |
868 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
869 | where |
870 | Self: 'b, |
871 | { |
872 | Ok(Cow::Owned( |
873 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
874 | )) |
875 | } |
876 | |
877 | #[inline ] |
878 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
879 | where |
880 | Self: Sized, |
881 | F: FnOnce(&CStr) -> io::Result<T>, |
882 | { |
883 | with_c_str(self, f) |
884 | } |
885 | } |
886 | |
887 | #[cfg (feature = "alloc" )] |
888 | impl Arg for &Vec<u8> { |
889 | #[inline ] |
890 | fn as_str(&self) -> io::Result<&str> { |
891 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
892 | } |
893 | |
894 | #[cfg (feature = "alloc" )] |
895 | #[inline ] |
896 | fn to_string_lossy(&self) -> Cow<'_, str> { |
897 | String::from_utf8_lossy(self) |
898 | } |
899 | |
900 | #[cfg (feature = "alloc" )] |
901 | #[inline ] |
902 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
903 | Ok(Cow::Owned( |
904 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
905 | )) |
906 | } |
907 | |
908 | #[cfg (feature = "alloc" )] |
909 | #[inline ] |
910 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
911 | where |
912 | Self: 'b, |
913 | { |
914 | Ok(Cow::Owned( |
915 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
916 | )) |
917 | } |
918 | |
919 | #[inline ] |
920 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
921 | where |
922 | Self: Sized, |
923 | F: FnOnce(&CStr) -> io::Result<T>, |
924 | { |
925 | with_c_str(self, f) |
926 | } |
927 | } |
928 | |
929 | #[cfg (feature = "alloc" )] |
930 | impl Arg for Vec<u8> { |
931 | #[inline ] |
932 | fn as_str(&self) -> io::Result<&str> { |
933 | str::from_utf8(self).map_err(|_utf8_err| io::Errno::INVAL) |
934 | } |
935 | |
936 | #[cfg (feature = "alloc" )] |
937 | #[inline ] |
938 | fn to_string_lossy(&self) -> Cow<'_, str> { |
939 | String::from_utf8_lossy(self) |
940 | } |
941 | |
942 | #[cfg (feature = "alloc" )] |
943 | #[inline ] |
944 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
945 | Ok(Cow::Owned( |
946 | CString::new(self.as_slice()).map_err(|_cstr_err| io::Errno::INVAL)?, |
947 | )) |
948 | } |
949 | |
950 | #[cfg (feature = "alloc" )] |
951 | #[inline ] |
952 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
953 | where |
954 | Self: 'b, |
955 | { |
956 | Ok(Cow::Owned( |
957 | CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?, |
958 | )) |
959 | } |
960 | |
961 | #[inline ] |
962 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
963 | where |
964 | Self: Sized, |
965 | F: FnOnce(&CStr) -> io::Result<T>, |
966 | { |
967 | f(&CString::new(self).map_err(|_cstr_err| io::Errno::INVAL)?) |
968 | } |
969 | } |
970 | |
971 | #[cfg (feature = "itoa" )] |
972 | impl Arg for DecInt { |
973 | #[inline ] |
974 | fn as_str(&self) -> io::Result<&str> { |
975 | Ok(self.as_str()) |
976 | } |
977 | |
978 | #[cfg (feature = "alloc" )] |
979 | #[inline ] |
980 | fn to_string_lossy(&self) -> Cow<'_, str> { |
981 | Cow::Borrowed(self.as_str()) |
982 | } |
983 | |
984 | #[cfg (feature = "alloc" )] |
985 | #[inline ] |
986 | fn as_cow_c_str(&self) -> io::Result<Cow<'_, CStr>> { |
987 | Ok(Cow::Borrowed(self.as_c_str())) |
988 | } |
989 | |
990 | #[cfg (feature = "alloc" )] |
991 | #[inline ] |
992 | fn into_c_str<'b>(self) -> io::Result<Cow<'b, CStr>> |
993 | where |
994 | Self: 'b, |
995 | { |
996 | Ok(Cow::Owned(self.as_c_str().to_owned())) |
997 | } |
998 | |
999 | #[inline ] |
1000 | fn into_with_c_str<T, F>(self, f: F) -> io::Result<T> |
1001 | where |
1002 | Self: Sized, |
1003 | F: FnOnce(&CStr) -> io::Result<T>, |
1004 | { |
1005 | f(self.as_c_str()) |
1006 | } |
1007 | } |
1008 | |
1009 | /// Runs a closure with `bytes` passed in as a `&CStr`. |
1010 | #[allow (unsafe_code, clippy::int_plus_one)] |
1011 | #[inline ] |
1012 | fn with_c_str<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
1013 | where |
1014 | F: FnOnce(&CStr) -> io::Result<T>, |
1015 | { |
1016 | // Most paths are less than `SMALL_PATH_BUFFER_SIZE` long. The rest can go |
1017 | // through the dynamic allocation path. If you're opening many files in a |
1018 | // directory with a long path, consider opening the directory and using |
1019 | // `openat` to open the files under it, which will avoid this, and is often |
1020 | // faster in the OS as well. |
1021 | |
1022 | // Test with >= so that we have room for the trailing NUL. |
1023 | if bytes.len() >= SMALL_PATH_BUFFER_SIZE { |
1024 | return with_c_str_slow_path(bytes, f); |
1025 | } |
1026 | |
1027 | // Taken from |
1028 | // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
1029 | let mut buf = MaybeUninit::<[u8; SMALL_PATH_BUFFER_SIZE]>::uninit(); |
1030 | let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
1031 | |
1032 | // This helps test our safety condition below. |
1033 | debug_assert!(bytes.len() + 1 <= SMALL_PATH_BUFFER_SIZE); |
1034 | |
1035 | // SAFETY: `bytes.len() < SMALL_PATH_BUFFER_SIZE` which means we have space |
1036 | // for `bytes.len() + 1` u8s: |
1037 | unsafe { |
1038 | ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
1039 | buf_ptr.add(bytes.len()).write(0); |
1040 | } |
1041 | |
1042 | // SAFETY: We just wrote the bytes above and they will remain valid for the |
1043 | // duration of `f` b/c buf doesn't get dropped until the end of the |
1044 | // function. |
1045 | match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { |
1046 | Ok(s) => f(s), |
1047 | Err(_) => Err(io::Errno::INVAL), |
1048 | } |
1049 | } |
1050 | |
1051 | /// The slow path which handles any length. In theory OS's only support up to |
1052 | /// `PATH_MAX`, but we let the OS enforce that. |
1053 | #[allow (unsafe_code, clippy::int_plus_one)] |
1054 | #[cold ] |
1055 | fn with_c_str_slow_path<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
1056 | where |
1057 | F: FnOnce(&CStr) -> io::Result<T>, |
1058 | { |
1059 | #[cfg (feature = "alloc" )] |
1060 | { |
1061 | f(&CString::new(bytes).map_err(|_cstr_err| io::Errno::INVAL)?) |
1062 | } |
1063 | |
1064 | #[cfg (not(feature = "alloc" ))] |
1065 | { |
1066 | #[cfg (libc)] |
1067 | const LARGE_PATH_BUFFER_SIZE: usize = libc::PATH_MAX as usize; |
1068 | #[cfg (linux_raw)] |
1069 | const LARGE_PATH_BUFFER_SIZE: usize = linux_raw_sys::general::PATH_MAX as usize; |
1070 | |
1071 | // Taken from |
1072 | // <https://github.com/rust-lang/rust/blob/a00f8ba7fcac1b27341679c51bf5a3271fa82df3/library/std/src/sys/common/small_c_string.rs> |
1073 | let mut buf = MaybeUninit::<[u8; LARGE_PATH_BUFFER_SIZE]>::uninit(); |
1074 | let buf_ptr = buf.as_mut_ptr().cast::<u8>(); |
1075 | |
1076 | // This helps test our safety condition below. |
1077 | if bytes.len() + 1 > LARGE_PATH_BUFFER_SIZE { |
1078 | return Err(io::Errno::NAMETOOLONG); |
1079 | } |
1080 | |
1081 | // SAFETY: `bytes.len() < LARGE_PATH_BUFFER_SIZE` which means we have |
1082 | // space for `bytes.len() + 1` u8s: |
1083 | unsafe { |
1084 | ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); |
1085 | buf_ptr.add(bytes.len()).write(0); |
1086 | } |
1087 | |
1088 | // SAFETY: We just wrote the bytes above and they will remain valid for |
1089 | // the duration of `f` b/c buf doesn't get dropped until the end of the |
1090 | // function. |
1091 | match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) |
1092 | { |
1093 | Ok(s) => f(s), |
1094 | Err(_) => Err(io::Errno::INVAL), |
1095 | } |
1096 | } |
1097 | } |
1098 | |