Skip to main content

turbo_tasks_fs/
util.rs

1use std::{
2    io::{self, ErrorKind},
3    path::{Path, PathBuf},
4};
5
6use anyhow::{Context, Result, anyhow};
7use turbo_tasks::ResolvedVc;
8
9use crate::{DiskFileSystem, FileSystemPath};
10
11/// Converts a disk access `Result<T>` into a `Result<Some<T>>`, where a [`ErrorKind::NotFound`] (or
12/// [`ErrorKind::InvalidFilename`]) error results in a [`None`] value. This is purely to reduce
13/// boilerplate code comparing [`ErrorKind::NotFound`] errors against all other errors.
14pub fn extract_disk_access<T>(value: io::Result<T>, path: &Path) -> Result<Option<T>> {
15    match value {
16        Ok(v) => Ok(Some(v)),
17        Err(e) if matches!(e.kind(), ErrorKind::NotFound | ErrorKind::InvalidFilename) => Ok(None),
18        // ast-grep-ignore: no-context-format
19        Err(e) => Err(anyhow!(e).context(format!("reading file {}", path.display()))),
20    }
21}
22
23pub async fn uri_from_file(root: FileSystemPath, path: Option<&str>) -> Result<String> {
24    let root_fs = root.fs;
25    let root_fs = &*ResolvedVc::try_downcast_type::<DiskFileSystem>(root_fs)
26        .context("Expected root to have a DiskFileSystem")?
27        .await?;
28
29    let path = match path {
30        Some(path) => root.join(path)?,
31        None => root,
32    };
33
34    Ok(uri_from_path_buf(root_fs.to_sys_path(&path)))
35}
36
37#[cfg(not(target_os = "windows"))]
38pub fn uri_from_path_buf(sys_path: PathBuf) -> String {
39    use turbo_unix_path::sys_to_unix;
40    let sys_path = sys_path.to_string_lossy();
41
42    format!(
43        "file://{}",
44        sys_to_unix(&sys_path)
45            .split('/')
46            .map(|s| urlencoding::encode(s))
47            .collect::<Vec<_>>()
48            .join("/")
49    )
50}
51
52#[cfg(target_os = "windows")]
53pub fn uri_from_path_buf(sys_path: PathBuf) -> String {
54    let raw_path = sys_path.to_string_lossy().to_string();
55    let normalized_path = raw_path.replace('\\', "/");
56
57    let mut segments = normalized_path.split('/');
58
59    let first = segments.next().unwrap_or_default(); // e.g., "C:"
60    let encoded_path = std::iter::once(first.to_string()) // keep "C:" intact
61        .chain(segments.map(|s| urlencoding::encode(s).into_owned()))
62        .collect::<Vec<_>>()
63        .join("/");
64
65    format!("file:///{}", encoded_path)
66}