runner_core/packs/resolver/
fs.rs1use 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}