2016-03-01 19:30:42 +00:00
|
|
|
module vibe.core.path;
|
|
|
|
|
2016-11-02 20:00:13 +00:00
|
|
|
import std.exception : enforce;
|
|
|
|
|
2016-10-25 06:56:05 +00:00
|
|
|
static import std.path;
|
|
|
|
|
2016-11-02 20:00:13 +00:00
|
|
|
|
2016-03-01 19:30:42 +00:00
|
|
|
struct Path {
|
2016-11-02 20:00:13 +00:00
|
|
|
@safe:
|
2016-03-01 19:30:42 +00:00
|
|
|
private string m_path;
|
|
|
|
|
|
|
|
this(string p)
|
2016-11-02 20:00:13 +00:00
|
|
|
nothrow {
|
2016-03-01 19:30:42 +00:00
|
|
|
m_path = p;
|
|
|
|
}
|
|
|
|
|
2016-11-02 20:00:13 +00:00
|
|
|
@property bool absolute()
|
|
|
|
{
|
|
|
|
import std.algorithm.searching : startsWith;
|
|
|
|
// FIXME: Windows paths!
|
|
|
|
return m_path.startsWith('/');
|
|
|
|
}
|
|
|
|
|
|
|
|
@property void endsWithSlash(bool v)
|
|
|
|
{
|
|
|
|
import std.algorithm.searching : endsWith;
|
|
|
|
|
|
|
|
if (!m_path.endsWith('/') && v) m_path ~= '/';
|
|
|
|
else if (m_path.endsWith('/') && !v) m_path = m_path[0 .. $-1]; // FIXME: "/test//" -> "/test/"
|
|
|
|
}
|
|
|
|
|
|
|
|
@property bool empty() const { return m_path.length == 0 || m_path == "/"; }
|
|
|
|
|
|
|
|
|
|
|
|
Path parentPath()
|
|
|
|
{
|
|
|
|
import std.string : lastIndexOf;
|
|
|
|
auto idx = m_path.lastIndexOf('/');
|
|
|
|
version (Windows) {
|
|
|
|
auto idx2 = m_path.lastIndexOf('\\');
|
|
|
|
if (idx2 > idx) idx = idx2;
|
|
|
|
}
|
|
|
|
enforce(idx > 0, "Path has no parent path.");
|
|
|
|
return Path(m_path[0 .. idx+1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Path relativeTo(Path p) { assert(false, "TODO!"); }
|
|
|
|
Path relativeToWeb(Path p) { assert(false, "TODO!"); }
|
|
|
|
|
|
|
|
void normalize()
|
|
|
|
{
|
|
|
|
assert(false, "TODO!");
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: this should decompose the two paths into their parts and compare the part sequence
|
|
|
|
bool startsWith(Path other) const {
|
|
|
|
import std.algorithm.searching : startsWith;
|
|
|
|
return m_path[].startsWith(other.m_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
string toString() const nothrow { return m_path; }
|
|
|
|
|
|
|
|
string toNativeString() const nothrow { return m_path; }
|
|
|
|
|
|
|
|
PathEntry opIndex(size_t idx)
|
|
|
|
const {
|
|
|
|
import std.string : indexOf;
|
2016-03-01 19:30:42 +00:00
|
|
|
|
2016-11-02 20:00:13 +00:00
|
|
|
string s = m_path;
|
|
|
|
while (true) {
|
|
|
|
auto sidx = m_path.indexOf('/');
|
|
|
|
if (idx == 0) break;
|
|
|
|
enforce(sidx > 0, "Index out of bounds");
|
|
|
|
s = s[sidx+1 .. $];
|
|
|
|
idx--;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto sidx = s.indexOf('/');
|
|
|
|
return PathEntry(s[0 .. sidx >= 0 ? sidx : $]);
|
|
|
|
}
|
2016-10-25 06:56:05 +00:00
|
|
|
|
|
|
|
Path opBinary(string op : "~")(string subpath) { return this ~ Path(subpath); }
|
|
|
|
Path opBinary(string op : "~")(Path subpath) { return Path(std.path.buildPath(m_path, subpath.toString())); }
|
2016-03-01 19:30:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct PathEntry {
|
|
|
|
nothrow: @safe:
|
|
|
|
private string m_name;
|
|
|
|
|
|
|
|
this(string name)
|
|
|
|
{
|
|
|
|
m_name = name;
|
|
|
|
}
|
2016-11-02 20:00:13 +00:00
|
|
|
|
|
|
|
alias toString this;
|
|
|
|
|
|
|
|
string toString() { return m_name; }
|
|
|
|
}
|
|
|
|
|
|
|
|
private enum PathType {
|
|
|
|
unix,
|
|
|
|
windows,
|
|
|
|
inet
|
|
|
|
}
|
|
|
|
|
|
|
|
private struct PathEntryRange(PathType TYPE) {
|
|
|
|
enum type = TYPE;
|
|
|
|
private {
|
|
|
|
string m_path;
|
|
|
|
PathEntry m_front;
|
|
|
|
}
|
|
|
|
|
|
|
|
this(string path)
|
|
|
|
{
|
|
|
|
m_path = path;
|
|
|
|
skipLeadingSeparator();
|
|
|
|
}
|
|
|
|
|
|
|
|
@property bool empty() const { return m_path.length == 0; }
|
|
|
|
|
|
|
|
@property ref const(PathEntry) front() const { return m_front; }
|
|
|
|
|
|
|
|
void popFront()
|
|
|
|
{
|
|
|
|
import std.string : indexOf;
|
|
|
|
|
|
|
|
auto idx = m_path.indexOf('/');
|
|
|
|
static if (type == PathType.windows) {
|
|
|
|
if (idx >= 0) {
|
|
|
|
auto idx2 = m_path[0 .. idx].indexOf('\\');
|
|
|
|
if (idx2 >= 0) idx = idx2;
|
|
|
|
} else idx = m_path.indexOf('\\');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx < 0) {
|
|
|
|
m_front = PathEntry(m_path);
|
|
|
|
m_path = null;
|
|
|
|
} else {
|
|
|
|
m_front = PathEntry(m_path[0 .. idx]);
|
|
|
|
m_path = m_path[idx+1 .. $];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void skipLeadingSeparator()
|
|
|
|
{
|
|
|
|
import std.algorithm.searching : startsWith;
|
|
|
|
|
|
|
|
if (m_path.startsWith('/')) m_path = m_path[1 .. $];
|
|
|
|
else static if (type == PathType.windows) {
|
|
|
|
if (m_path.startsWith('\\')) m_path = m_path[1 .. $];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unittest {
|
|
|
|
import std.algorithm.comparison : equal;
|
|
|
|
|
|
|
|
assert(PathEntryRange!(PathType.unix)("hello/world").equal([PathEntry("hello"), PathEntry("world")]));
|
|
|
|
assert(PathEntryRange!(PathType.unix)("/hello/world/").equal([PathEntry("hello"), PathEntry("world")]));
|
|
|
|
assert(PathEntryRange!(PathType.unix)("hello\\world").equal([PathEntry("hello\\world")]));
|
|
|
|
assert(PathEntryRange!(PathType.windows)("hello/world").equal([PathEntry("hello"), PathEntry("world")]));
|
|
|
|
assert(PathEntryRange!(PathType.windows)("/hello/world/").equal([PathEntry("hello"), PathEntry("world")]));
|
|
|
|
assert(PathEntryRange!(PathType.windows)("hello\\w/orld").equal([PathEntry("hello"), PathEntry("w"), PathEntry("orld")]));
|
|
|
|
assert(PathEntryRange!(PathType.windows)("hello/w\\orld").equal([PathEntry("hello"), PathEntry("w"), PathEntry("orld")]));
|
2016-03-01 19:30:42 +00:00
|
|
|
}
|