Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement proper conversion between native paths and file URLs. #2285

Merged
merged 1 commit into from
Mar 29, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`));
}