turbo_tasks_fs/
util.rs

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