runner_core/packs/resolver/
fs.rs

1use std::path::PathBuf;
2
3use anyhow::{Context, Result, anyhow};
4use url::Url;
5
6use super::{FetchResponse, PackResolver};
7
8#[derive(Debug, Default)]
9pub struct FsResolver;
10
11impl FsResolver {
12    pub fn new() -> Self {
13        Self
14    }
15
16    fn parse_path(&self, locator: &str) -> Result<PathBuf> {
17        if let Some(stripped) = locator.strip_prefix("fs://") {
18            if stripped.starts_with('/')
19                || stripped.starts_with("./")
20                || stripped.starts_with("../")
21            {
22                return Ok(PathBuf::from(stripped));
23            }
24            if cfg!(windows) && stripped.chars().nth(1) == Some(':') {
25                return Ok(PathBuf::from(stripped));
26            }
27            let file_url = format!("file://{stripped}");
28            let url = Url::parse(&file_url).context("failed to parse fs:// locator as file URL")?;
29            return url
30                .to_file_path()
31                .map_err(|_| anyhow!("fs locator {locator} cannot be represented as a path"));
32        }
33        Ok(PathBuf::from(locator))
34    }
35}
36
37impl PackResolver for FsResolver {
38    fn scheme(&self) -> &'static str {
39        "fs"
40    }
41
42    fn fetch(&self, locator: &str) -> Result<FetchResponse> {
43        let path = self.parse_path(locator)?;
44        if !path.exists() {
45            anyhow::bail!("fs resolver: {} does not exist", path.display());
46        }
47        Ok(FetchResponse::from_path(path))
48    }
49}