vibe-core/source/vibe/core/path.d

166 lines
3.9 KiB
D
Raw Normal View History

module vibe.core.path;
import std.exception : enforce;
2016-10-25 06:56:05 +00:00
static import std.path;
struct Path {
@safe:
private string m_path;
this(string p)
nothrow {
m_path = p;
}
@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;
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())); }
}
struct PathEntry {
nothrow: @safe:
private string m_name;
this(string name)
{
m_name = name;
}
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")]));
}