#![allow(clippy::items_after_test_module)]
#[cfg(any(target_arch = "wasm32", test))]
use std::ffi::OsStr;
use std::path::PathBuf;
use url::Url;
use crate::specifier::SpecifierError;
pub fn url_to_path(input: &str) -> Result<PathBuf, SpecifierError> {
let url = Url::parse(input)?;
#[cfg(target_arch = "wasm32")]
{
Ok(to_file_path(&url).map_err(|_| SpecifierError::InvalidFileUrl)?)
}
#[cfg(not(target_arch = "wasm32"))]
{
url
.to_file_path()
.map_err(|_| SpecifierError::InvalidFileUrl)
}
}
#[cfg(any(target_arch = "wasm32", test))]
#[inline]
fn os_str_from_bytes(slice: &[u8]) -> &OsStr {
unsafe { std::mem::transmute(slice) }
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use url::Url;
use crate::url_to_path::to_file_path;
#[test]
fn test() {
let f = "/x/y/z/foo.js";
assert_eq!(
to_file_path(&Url::parse(&format!("file://{f}")).unwrap()),
Ok(PathBuf::from(f))
);
let f = "/bar.js";
assert_eq!(
to_file_path(&Url::parse(&format!("file://{f}")).unwrap()),
Ok(PathBuf::from(f))
);
}
}
#[cfg(any(target_arch = "wasm32", test))]
pub fn to_file_path(this: &Url) -> Result<PathBuf, ()> {
use url::Host;
if let Some(segments) = this.path_segments() {
let host = match this.host() {
None | Some(Host::Domain("localhost")) => None,
_ => return Err(()),
};
return file_url_segments_to_pathbuf(host, segments);
}
Err(())
}
#[allow(clippy::manual_is_ascii_check)]
#[cfg(any(target_arch = "wasm32", test))]
fn file_url_segments_to_pathbuf(
host: Option<&str>,
segments: core::str::Split<'_, char>,
) -> Result<PathBuf, ()> {
use percent_encoding::percent_decode;
if host.is_some() {
return Err(());
}
let mut bytes =
Vec::new()
;
for segment in segments {
bytes.push(b'/');
bytes.extend(percent_decode(segment.as_bytes()));
}
if bytes.len() > 2
&& matches!(bytes[bytes.len() - 2], b'a'..=b'z' | b'A'..=b'Z')
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
{
bytes.push(b'/');
}
let os_str = os_str_from_bytes(&bytes);
let path = PathBuf::from(os_str);
debug_assert!(
path.has_root(), "to_file_path() failed to produce an absolute Path"
);
Ok(path)
}