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 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
22pub async fn uri_from_file(root: FileSystemPath, path: Option<&str>) -> Result<String> {
23    let root_fs = root.fs;
24    let root_fs = &*ResolvedVc::try_downcast_type::<DiskFileSystem>(root_fs)
25        .context("Expected root to have a DiskFileSystem")?
26        .await?;
27
28    let path = match path {
29        Some(path) => root.join(path)?,
30        None => root,
31    };
32
33    Ok(uri_from_path_buf(root_fs.to_sys_path(&path)))
34}
35
36#[cfg(not(target_os = "windows"))]
37pub fn uri_from_path_buf(sys_path: PathBuf) -> String {
38    use turbo_unix_path::sys_to_unix;
39    let sys_path = sys_path.to_string_lossy();
40
41    format!(
42        "file://{}",
43        sys_to_unix(&sys_path)
44            .split('/')
45            .map(|s| urlencoding::encode(s))
46            .collect::<Vec<_>>()
47            .join("/")
48    )
49}
50
51#[cfg(target_os = "windows")]
52pub fn uri_from_path_buf(sys_path: PathBuf) -> String {
53    let raw_path = sys_path.to_string_lossy().to_string();
54    let normalized_path = raw_path.replace('\\', "/");
55
56    let mut segments = normalized_path.split('/');
57
58    let first = segments.next().unwrap_or_default(); // e.g., "C:"
59    let encoded_path = std::iter::once(first.to_string()) // keep "C:" intact
60        .chain(segments.map(|s| urlencoding::encode(s).into_owned()))
61        .collect::<Vec<_>>()
62        .join("/");
63
64    format!("file:///{}", encoded_path)
65}