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