Skip to content

Commit

Permalink
Implement proper conversion between native paths and file URLs.
Browse files Browse the repository at this point in the history
Properly handles UNC paths on Windows in addition to normal paths.
  • Loading branch information
s-ludwig committed Mar 28, 2019
1 parent 7890674 commit f897aa4
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 f897aa4

Please sign in to comment.