Skip to main content

compio_fs/
lib.rs

1//! Filesystem utilities.
2
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![allow(unused_features)]
5#![warn(missing_docs)]
6#![deny(rustdoc::broken_intra_doc_links)]
7#![doc(
8    html_logo_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
9)]
10#![doc(
11    html_favicon_url = "https://github.com/compio-rs/compio-logo/raw/refs/heads/master/generated/colored-bold.svg"
12)]
13#![cfg_attr(feature = "read_buf", feature(read_buf, core_io_borrowed_buf))]
14#![cfg_attr(
15    all(windows, feature = "windows_by_handle"),
16    feature(windows_by_handle)
17)]
18
19mod file;
20pub use file::*;
21
22mod open_options;
23pub use open_options::*;
24
25mod metadata;
26pub use metadata::*;
27
28mod stdio;
29pub use stdio::*;
30
31mod utils;
32pub use utils::*;
33
34#[cfg(dirfd)]
35mod dirfd;
36#[cfg(dirfd)]
37pub use dirfd::*;
38
39#[cfg(windows)]
40pub mod named_pipe;
41
42#[cfg(unix)]
43pub mod pipe;
44
45/// Providing functionalities to wait for readiness.
46#[deprecated(since = "0.12.0", note = "Use `compio::runtime::fd::AsyncFd` instead")]
47pub type AsyncFd<T> = compio_runtime::fd::AsyncFd<T>;
48
49use std::{future::Future, io};
50
51#[cfg(unix)]
52pub(crate) fn path_string(path: impl AsRef<std::path::Path>) -> io::Result<std::ffi::CString> {
53    use std::os::unix::ffi::OsStrExt;
54
55    std::ffi::CString::new(path.as_ref().as_os_str().as_bytes().to_vec()).map_err(|_| {
56        io::Error::new(
57            io::ErrorKind::InvalidInput,
58            "file name contained an unexpected NUL byte",
59        )
60    })
61}
62
63use compio_buf::{BufResult, IntoInner};
64use compio_driver::{SharedFd, op::AsyncifyFd};
65
66/// Run `f` on the blocking pool, reporting it to the console under `name`.
67///
68/// The tasks compio spawns itself are named, since their location points into
69/// compio rather than into the code that asked for the work. It still tells
70/// which fallback is running, so this is a plain `fn`: `#[track_caller]` is a
71/// no-op on an `async fn`, and every fallback would report this line instead
72/// of its own.
73#[allow(dead_code)] // Only some platforms have blocking fallbacks.
74#[track_caller]
75pub(crate) fn spawn_blocking_named<T: Send + 'static>(
76    name: &'static str,
77    f: impl (FnOnce() -> T) + Send + 'static,
78) -> impl Future<Output = T> {
79    use compio_runtime::{ResumeUnwind, SpawnMeta};
80
81    // Captured before the future, which is where the caller is lost.
82    let meta = SpawnMeta::capture().named(name);
83
84    async move {
85        compio_runtime::spawn_blocking_at(f, meta)
86            .await
87            .resume_unwind()
88            .expect("shouldn't be cancelled")
89    }
90}
91
92pub(crate) async fn spawn_blocking_with<T, R, F>(fd: SharedFd<T>, f: F) -> io::Result<R>
93where
94    T: Sync + 'static,
95    R: Send + 'static,
96    F: FnOnce(&T) -> io::Result<R> + Send + 'static,
97{
98    let op = AsyncifyFd::new(fd, move |fd: &T| match f(fd) {
99        Ok(res) => BufResult(Ok(0), Some(res)),
100        Err(e) => BufResult(Err(e), None),
101    });
102    let BufResult(res, meta) = compio_runtime::submit(op).await;
103    res?;
104    Ok(meta.into_inner().expect("result should be present"))
105}
106
107#[cfg(all(windows, dirfd))]
108pub(crate) async fn spawn_blocking_with2<T1, T2, R, F>(
109    fd1: SharedFd<T1>,
110    fd2: SharedFd<T2>,
111    f: F,
112) -> io::Result<R>
113where
114    T1: Sync + 'static,
115    T2: Sync + 'static,
116    R: Send + 'static,
117    F: FnOnce(&T1, &T2) -> io::Result<R> + Send + 'static,
118{
119    use compio_driver::op::AsyncifyFd2;
120
121    let op = AsyncifyFd2::new(fd1, fd2, move |fd1: &T1, fd2: &T2| match f(fd1, fd2) {
122        Ok(res) => BufResult(Ok(0), Some(res)),
123        Err(e) => BufResult(Err(e), None),
124    });
125    let BufResult(res, meta) = compio_runtime::submit(op).await;
126    res?;
127    Ok(meta.into_inner().expect("result should be present"))
128}