blob: 77c39452403e65e02dc273e9059a192d2dd8fbe0 [file] [log] [blame]
//! Intended for internal use only, but made public for testing. This SHOULD NOT be used or relied
//! upon.
use hyper;
use std::path::{Path, PathBuf, Component};
use url::Url;
use url::percent_encoding::{percent_encode, percent_decode, DEFAULT_ENCODE_SET};
use error::Error;
// TODO I'm pretty sure all of this path stuff is a mess, but at least the basics of
// windows will work, right? Right?
/// Converts a `Path` into a URL with scheme `file://`.
pub fn path_to_url(path: &Path) -> Result<Url, Error> {
// TODO handle sloppy to_string_lossy() calls
let path_str = path.components()
.fold(PathBuf::new(), |buf, c| match c {
Component::Normal(os_str) => {
buf.join(format!("{}",
percent_encode(os_str.to_string_lossy().as_bytes(),
DEFAULT_ENCODE_SET)))
}
Component::RootDir => buf.join("/"),
Component::Prefix(pref) => {
if cfg!(windows) {
buf.join("/").join(pref.as_os_str().to_string_lossy().into_owned())
} else {
buf
}
}
Component::CurDir => buf.join("."),
Component::ParentDir => buf.join(".."),
});
Url::parse(&format!("file://{}",
percent_encode(path_str.to_string_lossy().as_bytes(), DEFAULT_ENCODE_SET)))
.map_err(|e| Error::Generic(format!("{}", e)))
}
/// Converts a URL string (without scheme) into an OS specific path.
pub fn url_path_to_os_path(url_path: &str) -> Result<PathBuf, Error> {
let url_path = if cfg!(os = "windows") {
url_path.replace("/", r"\")
} else {
url_path.to_string()
};
let url_path = percent_decode(url_path.as_bytes())
.decode_utf8()
.map_err(|e| Error::Generic(format!("{}", e)))?
.into_owned();
Ok(Path::new(&url_path).to_path_buf())
}
pub fn url_path_to_os_path_components(url_path: &str) -> Result<Vec<String>, Error> {
let mut out = Vec::new();
for component in url_path.split("/") {
let component = percent_decode(component.as_bytes())
.decode_utf8()
.map_err(|e| Error::Generic(format!("Path component not utf-8: {:?}", e)))
?
.into_owned();
out.push(component);
}
Ok(out)
}
/// Converts a `url::Url` into a `hyper::Url`.
pub fn url_to_hyper_url(url: &Url) -> Result<hyper::Url, Error> {
Ok(hyper::Url::parse(url.as_str())?)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(not(target_os = "windows"))]
fn test_path_to_url_nix() {
let path = Path::new("/tmp/test");
assert_eq!(path_to_url(path),
Ok(Url::parse("file:///tmp/test").unwrap()));
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_path_to_url_spaces_nix() {
let path = Path::new("/tmp/test stuff");
assert_eq!(path_to_url(path),
Ok(Url::parse("file:///tmp/test%20stuff").unwrap()));
}
#[test]
#[cfg(target_os = "windows")]
fn test_path_to_url_win() {
let path = Path::new(r"C:\tmp\test");
assert_eq!(path_to_url(path),
Ok(Url::parse("file:///C:/tmp/test").unwrap()));
}
#[test]
#[cfg(target_os = "windows")]
fn test_path_to_url_spaces_win() {
let path = Path::new(r"C:\tmp\test stuff");
assert_eq!(path_to_url(path),
Ok(Url::parse("file:///C:/tmp/test%20stuff").unwrap()));
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_url_path_to_os_path_nix() {
let path = "/tmp/test";
assert_eq!(url_path_to_os_path(path), Ok(PathBuf::from("/tmp/test")));
}
#[test]
#[cfg(not(target_os = "windows"))]
fn test_url_path_to_os_path_percent_nix() {
let path = "/tmp/test%20stuff";
assert_eq!(url_path_to_os_path(path),
Ok(PathBuf::from("/tmp/test stuff")));
}
#[test]
#[cfg(target_os = "windows")]
fn test_url_path_to_os_path_win() {
let path = r"C:/tmp/test";
assert_eq!(url_path_to_os_path(path), Ok(PathBuf::from(r"C:\tmp\test")));
}
#[test]
#[cfg(target_os = "windows")]
fn test_url_path_to_os_path_spaces_win() {
let path = r"C:/tmp/test%20stuff";
assert_eq!(url_path_to_os_path(path),
Ok(PathBuf::from(r"C:\tmp\test stuff")));
}
#[test]
fn test_url_path_to_os_path_components() {
let path = "test/foo";
assert_eq!(url_path_to_os_path_components(path), Ok(vec!["test".into(), "foo".into()]));
let path = "test/foo%20bar";
assert_eq!(url_path_to_os_path_components(path), Ok(vec!["test".into(), "foo bar".into()]));
}
}