Coverage Report

Created: 2025-09-27 06:48

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