Skip to content

Commit

Permalink
Merge pull request #2285 from vibe-d/native_path_urls
Browse files Browse the repository at this point in the history
Implement proper conversion between native paths and file URLs.
  • Loading branch information
s-ludwig authored Mar 29, 2019
2 parents 7890674 + f897aa4 commit bc4dba6
Showing 1 changed file with 110 additions and 0 deletions.
110 changes: 110 additions & 0 deletions inet/vibe/inet/url.d
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,40 @@ struct URL {
{
this(schema, null, 0, cast(InetPath)path);
}

/** Constructs a "file:" URL from a native file system path.
Note that the path must be absolute. On Windows, both, paths starting
with a drive letter and UNC paths are supported.
*/
this(WindowsPath path)
{
import std.algorithm.iteration : map;
import std.range : chain, only, repeat;

enforce(path.absolute, "Only absolute paths can be converted to a URL.");

// treat UNC paths properly
if (path.startsWith(WindowsPath(`\\`))) {
auto segs = path.bySegment;
segs.popFront();
segs.popFront();
auto host = segs.front.name;
segs.popFront();

this("file", host, 0, InetPath(
only(InetPath.Segment("", '/'))
.chain(segs.map!(s => cast(InetPath.Segment)s))
));
} else this("file", host, 0, cast(InetPath)path);
}
/// ditto
this(PosixPath path)
{
enforce(path.absolute, "Only absolute paths can be converted to a URL.");

this("file", null, 0, cast(InetPath)path);
}
}

/** Constructs a URL from its string representation.
Expand Down Expand Up @@ -316,6 +350,37 @@ struct URL {
return dst.data;
}

/** Converts a "file" URL back to a native file system path.
*/
NativePath toNativePath()
const {
import std.algorithm.iteration : map;
import std.range : dropOne;

enforce(this.schema == "file", "Only file:// URLs can be converted to a native path.");

version (Windows) {
if (this.host.length) {
version (Have_vibe_core) {
auto p = NativePath(this.path
.bySegment
.dropOne
.map!(s => cast(WindowsPath.Segment)s)
);
} else {
auto p = NativePath(this.path
.bySegment
.dropOne
.array,
false);
}
return NativePath(`\\`~this.host) ~ p;
}
}

return cast(NativePath)this.path;
}

bool startsWith(const URL rhs) const {
if( m_schema != rhs.m_schema ) return false;
if( m_host != rhs.m_host ) return false;
Expand Down Expand Up @@ -502,3 +567,48 @@ unittest { // host name role in file:// URLs
assert(url.path == InetPath("/bar/baz"));
assert(url.toString() == "file://foo/bar/baz");
}

unittest { // native path <-> URL conversion
import std.exception : assertThrown;

version (Have_vibe_core)
auto url = URL(NativePath("/foo/bar"));
else
auto url = URL("file", "", 0, InetPath("/foo/bar"));
assert(url.schema == "file");
assert(url.host == "");
assert(url.path == InetPath("/foo/bar"));
assert(url.toNativePath == NativePath("/foo/bar"));

assertThrown(URL("http://example.org/").toNativePath);
version (Have_vibe_core) {
assertThrown(URL(NativePath("foo/bar")));
}
}

version (Windows) unittest { // Windows drive letter paths
version (Have_vibe_core)
auto url = URL(WindowsPath(`C:\foo`));
else
auto url = URL("file", "", 0, InetPath("/C:/foo"));
assert(url.schema == "file");
assert(url.host == "");
assert(url.path == InetPath("/C:/foo"));
auto p = url.toNativePath;
p.normalize();
assert(p == WindowsPath(`C:\foo`));
}

version (Windows) unittest { // UNC paths
version (Have_vibe_core)
auto url = URL(WindowsPath(`\\server\share\path`));
else
auto url = URL("file", "server", 0, InetPath("/share/path"));
assert(url.schema == "file");
assert(url.host == "server");
assert(url.path == InetPath("/share/path"));

auto p = url.toNativePath;
p.normalize(); // convert slash to backslash if necessary
assert(p == WindowsPath(`\\server\share\path`));
}

0 comments on commit bc4dba6

Please sign in to comment.