Created
August 25, 2015 06:40
-
-
Save rikkimax/e15984513b57714364f3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module dnetdev.vfs.path; | |
import std.experimental.allocator : IAllocator, makeArray, theAllocator, expandArray, shrinkArray, dispose, make; | |
private { | |
// santisization uses this | |
char[1024] charBuffer; | |
size_t[128] charIndexBuffer; | |
char[1024] tCopyBuffer; | |
} | |
/** | |
* Does not handle the encoding/decoding | |
*/ | |
struct URIAddress { | |
string value; | |
alias value this; | |
private { | |
IAllocator alloc; | |
char[][] arrbuffDirectories; | |
} | |
~this() { | |
alloc.dispose(cast(char[])value); | |
alloc.dispose(arrbuffDirectories); | |
} | |
/** | |
* Takes a string that represents a URI path | |
* | |
* Rerpresents a URI in the form of "<protName:[(//)(\\\\)]>[user[:pass]]@[host/ip[:port]][/][<dir/>...][file[.ext]]". | |
* Will automatically sanitise the input after duplicating it. | |
* | |
* Can be used to represent a file system entry (such as a Windows drive). But will be sanitised out as forward slash. | |
* | |
* Params: | |
* value = The path value, defaults to current working directory. | |
* alloc = The allocator to allocate return values as necssary. | |
*/ | |
this(string value=".", IAllocator alloc=theAllocator()) { | |
this.alloc = alloc; | |
arrbuffDirectories = alloc.makeArray!(char[])(0); | |
// duplicate the value | |
this.value = cast(immutable)alloc.makeArray!char(value.length); | |
(cast(char[])this.value[]) = cast(char[])value[]; | |
// sanitise the value | |
sanitise(); | |
} | |
@property { | |
/// | |
IAllocator allocator() { return alloc; } | |
/// | |
string connectionInfo() { | |
import std.string : indexOf; | |
// <protName://> <driveLetter:\\> | |
string prot = URIProtocolPrefix(value); | |
if (prot.length <= 1) | |
return null; | |
string value2 = value[prot.length + 3 .. $]; | |
// [user[:pass]]@[host/ip[:port]][/][<dir/>...][file[.ext]] | |
ptrdiff_t i; | |
if ((i = value2.indexOf("/")) >= 0) { | |
return value[0 .. i + prot.length + 3]; | |
} else { | |
return value; | |
} | |
} | |
/// | |
string protocol() { | |
string prot = URIProtocolPrefix(value); | |
string auth = URIAuthentication(value); | |
if (prot.length == 1 && auth is null) | |
return null; | |
else | |
return prot; | |
} | |
/// | |
string driveLetter() { | |
string prot = URIProtocolPrefix(value); | |
string auth = URIAuthentication(value); | |
if (prot.length == 1 && auth is null) | |
return prot; | |
else | |
return null; | |
} | |
/// | |
string username() { | |
import std.string : indexOf; | |
string prefix = URIAuthentication(value); | |
ptrdiff_t i; | |
if ((i = prefix.indexOf(":")) > 0) { | |
return prefix[0 .. i]; | |
} else if (i == 0) { | |
return null; | |
} else { | |
return prefix; | |
} | |
} | |
/// | |
string password() { | |
import std.string : indexOf; | |
string prefix = URIAuthentication(value); | |
ptrdiff_t i; | |
if ((i = prefix.indexOf(":")) >= 0) | |
if (prefix.length - (i + 1) != 0) | |
return prefix[i + 1 .. $]; | |
return null; | |
} | |
/// | |
ushort port() { | |
import std.string : indexOf; | |
import std.conv : to; | |
string prefix = URIConnection(value); | |
ptrdiff_t i; | |
if ((i = prefix.indexOf(":")) > 0 && prefix.length - i > 1) { | |
return to!ushort(prefix[i + 1 .. $]); | |
} else { | |
return 0; | |
} | |
} | |
/// | |
immutable(string[]) parts() { | |
size_t numDir; | |
string addr = URIEntries(value); | |
if (addr.length > 0) { | |
foreach(i, c; addr) { | |
if (i > 0 && i < addr.length-1 && c == '/') { | |
numDir++; | |
} | |
} | |
if (addr[$-1] != '/') | |
numDir++; | |
} | |
if (arrbuffDirectories.length < numDir) | |
alloc.expandArray(arrbuffDirectories, numDir - arrbuffDirectories.length); | |
string t; | |
size_t numDirI; | |
size_t lastI; | |
if (addr.length > 0 && addr[0] == '/') | |
lastI = 1; | |
foreach(i, c; addr) { | |
if (c == '/' && i > 0) { | |
arrbuffDirectories[numDirI] = cast(char[])t; | |
numDirI++; | |
t = null; | |
lastI = i + 1; | |
} else { | |
t = addr[lastI .. i + 1]; | |
} | |
} | |
if (t !is null) { | |
arrbuffDirectories[numDirI] = cast(char[])t; | |
} | |
return cast(immutable(string[]))arrbuffDirectories[0 .. numDir]; | |
} | |
immutable(string[]) partsStairCase(bool reverse=false) { | |
import std.string : indexOf; | |
size_t startI; | |
string addr = URIEntries(value); | |
string[] ret = cast(string[])parts(); | |
foreach(i, part; ret) { | |
startI += addr[startI .. $].indexOf(part) + part.length; | |
ret[i] = addr[0 .. startI]; | |
} | |
if (reverse) { | |
foreach(i1; 0 .. ret.length / 2) { | |
size_t i2 = ret.length - (i1 + 1); | |
if (i2 != i1) { | |
string v1 = ret[i1]; | |
string v2 = ret[i2]; | |
ret[i2] = v1; | |
ret[i1] = v2; | |
} | |
} | |
} | |
return cast(immutable)ret; | |
} | |
} | |
/// | |
void sanitise() { | |
import std.uni : toUpper; | |
char[] temp = cast(char[])value; | |
// make sure first directory entry starts with either a drive, root(/), . if not a protocol/drive or ~ | |
if (URIConnection(value) is null) { | |
if (driveLetter !is null) { | |
temp[0] = cast(char)toUpper(temp[0]); | |
temp[2] = '\\'; | |
temp[3] = '\\'; | |
} else if (temp.length > 0 && temp[0] == '~') { | |
if (temp.length > 1 && !(temp[1] == '/' || temp[1] == '\\')) { | |
char[] temp2 = alloc.makeArray!char(temp.length + 1); | |
temp2[0] = temp[0]; | |
temp2[1] = '/'; | |
temp2[2 .. $] = temp[1 .. $]; | |
alloc.dispose(temp); | |
temp = temp2; | |
value = cast(immutable)temp; | |
} | |
} else if (temp.length == 0) { | |
alloc.expandArray(temp, 1); | |
temp[0] = '/'; | |
} | |
} else if (URIProtocolPrefix(value) !is null) { | |
size_t offset = URIProtocolPrefix(value).length; | |
temp[offset + 1] = '/'; | |
temp[offset + 2] = '/'; | |
} | |
// make directory seperator only / | |
foreach(ref c; cast(char[])URIEntries(value)) { | |
if (c == '\\') | |
c = '/'; | |
} | |
// remove last / if added | |
if (temp[$-1] == '/' && temp.length > 1) | |
alloc.shrinkArray(temp, 1); | |
// work around, should not be needed | |
value = cast(immutable)temp; | |
size_t dashes; | |
size_t i = URIProtocolPrefix(value).length; | |
if (i == 1 && temp.length > 4 && temp[4] == '/') { | |
char[] temp2 = charBuffer[0 .. temp.length - 5]; | |
temp2[] = temp[5 .. $]; | |
temp[4 .. temp2.length+4] = temp2[]; | |
temp = temp[0 .. temp2.length+4]; | |
} | |
if (i > 0) | |
i += 3; // <prot>:// | |
for(;;) { | |
if (i >= temp.length) | |
break; | |
if (temp[i] == '/') { | |
i++; | |
dashes++; | |
} else if (dashes > 1) { | |
char[] temp2 = charBuffer[0 .. temp.length - (dashes-1)]; | |
temp2[0 .. i - dashes] = temp[0 .. i - dashes]; | |
temp2[i-dashes .. $] = temp[i-(dashes-1) .. $]; | |
temp[0 .. temp2.length] = temp2[]; | |
temp = temp[0 .. temp2.length]; | |
i -= (dashes-1); | |
//i++; | |
dashes = 0; | |
} else { | |
i++; | |
dashes = 0; | |
} | |
} | |
// work around, should not be needed | |
value = cast(immutable)temp; | |
} | |
/// | |
URIAddress expand(URIAddress homeDirectory, URIAddress cwd) { | |
import std.string : indexOf; | |
string prot = URIProtocolPrefix(value); | |
string temp = value; | |
char[] retbuf; | |
size_t offsetDirs; | |
// grabs <protName://> | |
if (prot.length > 1) { | |
ptrdiff_t offseti; | |
retbuf = charBuffer[0 .. prot.length + 3]; | |
retbuf[0 .. prot.length] = prot[0 .. 3]; | |
retbuf[prot.length .. $] = "://"; | |
temp = temp[prot.length + 3 .. $]; | |
offseti = temp.indexOf("/"); | |
// assuming sanitised correctly then this will /always/ exist | |
assert(offseti >= 0); | |
// grabs [user[:pass]]@[host/ip[:port]] | |
retbuf = charBuffer[0 .. retbuf.length + offseti]; | |
retbuf[$-offseti .. $] = temp[0 .. offseti]; | |
temp = temp[offseti .. $]; | |
offsetDirs = retbuf.length; | |
} | |
if (temp[0] == '/') | |
temp = temp[1 .. $]; | |
if (temp[0] == '.' || temp[0] == '~') { | |
string replaceTo; | |
// perform expansion | |
if (temp[0] == '.') { | |
if (URIProtocolPrefix(cwd) !is null && prot.length > 0) | |
throw new Exception("Protocol/drive letter as directory cannot be used in expansion"); | |
if (cwd.driveLetter !is null) { | |
replaceTo = cwd.value; | |
offsetDirs = 4; | |
} else { | |
replaceTo = URIEntries(cwd); | |
} | |
} else if (temp[0] == '~') { | |
if (URIProtocolPrefix(homeDirectory) !is null && prot.length > 0) | |
throw new Exception("Protocol/drive letter as directory cannot be used in expansion"); | |
if (homeDirectory.driveLetter !is null) { | |
replaceTo = homeDirectory.value; | |
offsetDirs = 4; | |
} else { | |
replaceTo = URIEntries(homeDirectory); | |
} | |
} | |
if ((replaceTo.length > 0 && replaceTo[0] == '/') || retbuf.length == 0) { | |
retbuf = charBuffer[0 .. retbuf.length + replaceTo.length]; | |
} else { | |
retbuf = charBuffer[0 .. retbuf.length + replaceTo.length + 1]; | |
retbuf[$-(replaceTo.length+1)] = '/'; | |
} | |
retbuf[$-replaceTo.length .. $] = replaceTo[]; | |
temp = temp[1 .. $]; | |
} | |
if (temp.length > 0 && temp[0] == '/') { | |
retbuf = charBuffer[0 .. retbuf.length + temp.length]; | |
} else { | |
retbuf = charBuffer[0 .. retbuf.length + (temp.length + 1)]; | |
retbuf[$-(temp.length + 1)] = '/'; | |
} | |
retbuf[$-temp.length .. $] = temp[]; | |
// expansion of parent and current directory is simple. | |
// current directory gets removed out right. | |
// parent directory removes it and its parent node if it should exist. | |
// C://dir1/dir2/../../dir3 C://dir3 | |
URIAddress* addrt = alloc.make!URIAddress(cast(string)retbuf, alloc); | |
retbuf = charBuffer[0 .. addrt.value.length]; | |
retbuf[] = addrt.value[]; | |
size_t skipDirs; | |
auto parts = addrt.parts; | |
size_t[] ibuffers; | |
foreach_reverse(i, p; parts) { | |
if (p == ".") continue; | |
else if (p == "..") { | |
skipDirs++; | |
continue; | |
} else if (skipDirs > 0) { | |
skipDirs--; | |
continue; | |
} | |
ibuffers = charIndexBuffer[0 .. ibuffers.length + 1]; | |
ibuffers[$-1] = i; | |
} | |
retbuf = retbuf[0 .. offsetDirs]; | |
// prot://user:pass@server:port/ | |
foreach_reverse(i; ibuffers) { | |
size_t len = parts[i].length; | |
retbuf = charBuffer[0 .. retbuf.length + len + 1]; | |
retbuf[$-(len + 1)] = '/'; | |
retbuf[$-len .. $] = parts[i]; | |
} | |
alloc.dispose(addrt); | |
try { | |
return URIAddress(cast(string)retbuf, alloc); | |
} catch(Error e) { | |
assert(0, retbuf); | |
} | |
} | |
/// | |
URIAddress parent() { | |
import std.string : indexOf; | |
string current = URIEntries(value); | |
size_t start; | |
foreach_reverse(i, c; current) { | |
if (c == '/') { | |
start = i; | |
break; | |
} | |
} | |
return URIAddress(value[0 .. value.indexOf(current) + start], alloc); | |
} | |
/// | |
URIAddress subPath(URIAddress sub) { | |
string suff = URIEntries(sub); | |
char[] buff = tCopyBuffer[0 .. value.length + 1]; | |
buff[0 .. $-1] = value[]; | |
buff[$-1] = '/'; | |
buff = tCopyBuffer[0 .. buff.length + suff.length]; | |
buff[$-suff.length .. $] = suff[]; | |
return URIAddress(cast(string)buff, alloc); | |
} | |
/// | |
URIAddress sibling(URIAddress subling) { | |
import std.string : indexOf; | |
string current = URIEntries(value); | |
string suff = URIEntries(subling); | |
size_t start; | |
foreach_reverse(i, c; current) { | |
if (c == '/') { | |
start = i; | |
break; | |
} | |
} | |
string value2 = value[0 .. value.indexOf(current) + start]; | |
char[] buff = tCopyBuffer[0 .. value2.length + 1]; | |
buff[0 .. $-1] = value2[]; | |
buff[$-1] = '/'; | |
buff = tCopyBuffer[0 .. buff.length + suff.length]; | |
buff[$-suff.length .. $] = suff[]; | |
return URIAddress(cast(string)buff, alloc); | |
} | |
} | |
/// | |
unittest { | |
assert(URIAddress("").connectionInfo is null); | |
assert(URIAddress("/abc").connectionInfo is null); | |
assert(URIAddress("/abc/def").connectionInfo is null); | |
assert(URIAddress("c://test.txt").connectionInfo is null); | |
assert(URIAddress("smb://test").connectionInfo == "smb://test"); | |
assert(URIAddress("smb://test/what").connectionInfo == "smb://test"); | |
assert(URIAddress("smb://user@test").connectionInfo == "smb://user@test"); | |
assert(URIAddress("smb://user:pass@test").connectionInfo == "smb://user:pass@test"); | |
assert(URIAddress("smb://user:pass@test/dir1/dir2").connectionInfo == "smb://user:pass@test"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("").value == "/"); | |
assert(URIAddress("smb:\\\\test/what") == "smb://test/what"); | |
assert(URIAddress("smb://test/what") == "smb://test/what"); | |
assert(URIAddress("~/test.txt") == "~/test.txt"); | |
assert(URIAddress("~\\test.txt") == "~/test.txt"); | |
assert(URIAddress("~test.txt") == "~/test.txt"); | |
assert(URIAddress("c:\\\\test.txt") == "C:\\\\test.txt"); | |
assert(URIAddress("c://test.txt") == "C:\\\\test.txt"); | |
assert(URIAddress("/test\\abc") == "/test/abc"); | |
assert(URIAddress("/") == "/"); | |
assert(URIAddress("/test/") == "/test"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("smb://:mypass@server").username is null); | |
assert(URIAddress("smb://test@server").username == "test"); | |
assert(URIAddress("smb://test:mypass@server").username == "test"); | |
assert(URIAddress("smb://test:@server").username == "test"); | |
assert(URIAddress("smb://:mypass@server").password == "mypass"); | |
assert(URIAddress("smb://test@server").password is null); | |
assert(URIAddress("smb://test:mypass@server").password == "mypass"); | |
assert(URIAddress("smb://test:@server").password is null); | |
} | |
/// | |
unittest { | |
assert(URIAddress("smb://server:89").port == 89); | |
assert(URIAddress("smb://server:").port == 0); | |
assert(URIAddress("smb://server").port == 0); | |
} | |
/// | |
unittest { | |
URIAddress addr = URIAddress("/dir1/file.txt"); | |
assert(addr.parts.length == 2); | |
assert(addr.parts[0] == "dir1"); | |
assert(addr.parts[1] == "file.txt"); | |
addr.value = "/dir1/dir2/file.txt"; | |
addr.sanitise(); | |
assert(addr.parts.length == 3); | |
assert(addr.parts[0] == "dir1"); | |
assert(addr.parts[1] == "dir2"); | |
assert(addr.parts[2] == "file.txt"); | |
addr.value = "/dir1/file.txt"; | |
addr.sanitise(); | |
assert(addr.parts.length == 2); | |
assert(addr.parts[0] == "dir1"); | |
assert(addr.parts[1] == "file.txt"); | |
assert(addr.arrbuffDirectories.length == 3); | |
} | |
/// | |
unittest { | |
URIAddress addr = URIAddress("smb://server/dir1/file.txt"); | |
assert(addr.parts.length == 2); | |
assert(addr.parts[0] == "dir1"); | |
assert(addr.parts[1] == "file.txt"); | |
} | |
/// | |
unittest { | |
URIAddress addr = URIAddress("/dir1/dir2/file.ext"); | |
immutable(string[]) parts = addr.partsStairCase; | |
assert(parts.length == 3); | |
assert(parts[0] == "/dir1"); | |
assert(parts[1] == "/dir1/dir2"); | |
assert(parts[2] == "/dir1/dir2/file.ext"); | |
} | |
/// | |
unittest { | |
URIAddress addr = URIAddress("/dir1/dir2/file.ext"); | |
immutable(string[]) parts = addr.partsStairCase(true); | |
assert(parts.length == 3); | |
assert(parts[0] == "/dir1/dir2/file.ext"); | |
assert(parts[1] == "/dir1/dir2"); | |
assert(parts[2] == "/dir1"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("./file.ext") == "./file.ext"); | |
assert(URIAddress(".file.ext") == ".file.ext"); | |
assert(URIAddress("~/file.ext") == "~/file.ext"); | |
assert(URIAddress("~file.ext") == "~/file.ext"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("/dir1//dir2/file.ext") == "/dir1/dir2/file.ext"); | |
assert(URIAddress("C://dir1//dir2/file.ext") == "C:\\\\dir1/dir2/file.ext"); | |
assert(URIAddress("C:\\\\/dir1//dir2/file.ext") == "C:\\\\dir1/dir2/file.ext"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("./test/file.ext").expand(URIAddress("/"), URIAddress("C:\\\\")) == "C:\\\\test/file.ext"); | |
assert(URIAddress("./test/file.ext").expand(URIAddress("/"), URIAddress("/etc")) == "/etc/test/file.ext"); | |
assert(URIAddress("~/test/file.ext").expand(URIAddress("C:\\\\"), URIAddress("/")) == "C:\\\\test/file.ext"); | |
assert(URIAddress("~/test/file.ext").expand(URIAddress("/etc"), URIAddress("/")) == "/etc/test/file.ext"); | |
assert(URIAddress("./test/file.ext").expand(URIAddress("C:\\\\"), URIAddress("/")) == "/test/file.ext"); | |
assert(URIAddress("./test/file.ext").expand(URIAddress("/etc"), URIAddress("/")) == "/test/file.ext"); | |
assert(URIAddress("~/test/file.ext").expand(URIAddress("/"), URIAddress("C:\\\\")) == "/test/file.ext"); | |
assert(URIAddress("~/test/file.ext").expand(URIAddress("/"), URIAddress("/etc")) == "/test/file.ext"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("./dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("C:\\\\")) == "C:\\\\dir3"); | |
assert(URIAddress("./dir1/dir2/../../dir3").expand(URIAddress("C:\\\\"), URIAddress("/")) == "/dir3"); | |
assert(URIAddress("~/dir1/dir2/../../dir3").expand(URIAddress("C:\\\\"), URIAddress("/")) == "C:\\\\dir3"); | |
assert(URIAddress("~/dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("C:\\\\")) == "/dir3"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("smb://server/./test").expand(URIAddress("/"), URIAddress("/")) == "smb://server/test"); | |
assert(URIAddress("smb://server/test").expand(URIAddress("/"), URIAddress("/")) == "smb://server/test"); | |
assert(URIAddress("smb://server/~/test").expand(URIAddress("/etc"), URIAddress("/")) == "smb://server/etc/test"); | |
assert(URIAddress("smb://server/./test").expand(URIAddress("/"), URIAddress("/etc")) == "smb://server/etc/test"); | |
assert(URIAddress("smb://server/dir1/dir2/../../dir3").expand(URIAddress("/"), URIAddress("/")) == "smb://server/dir3"); | |
} | |
/// | |
unittest { | |
try { | |
auto got = URIAddress("smb://server/~").expand(URIAddress("C:\\\\"), URIAddress("/")); | |
assert(0); | |
} catch (Exception e) { | |
assert(1); | |
} | |
try { | |
auto got = URIAddress("smb://server/.").expand(URIAddress("/"), URIAddress("C:\\\\")); | |
assert(0); | |
} catch (Exception e) { | |
assert(1); | |
} | |
} | |
/// | |
unittest { | |
assert(URIAddress("./dir1/dir2").parent == "./dir1"); | |
assert(URIAddress("~/dir1/dir2").parent == "~/dir1"); | |
assert(URIAddress("C:\\\\dir1/dir2").parent == "C:\\\\dir1"); | |
assert(URIAddress("smb://server/dir1/dir2").parent == "smb://server/dir1"); | |
assert(URIAddress("smb://server/dir1").parent == "smb://server"); | |
} | |
/// | |
unittest { | |
assert(URIAddress("/etc/dir1/").subPath(URIAddress("dir2")) == "/etc/dir1/dir2"); | |
assert(URIAddress("/etc/dir1/").subPath(URIAddress("../dir2")) == "/etc/dir1/../dir2"); | |
} | |
unittest { | |
assert(URIAddress("/etc/dir1/").sibling(URIAddress("dir2")) == "/etc/dir2"); | |
assert(URIAddress("/etc/dir1/").sibling(URIAddress("../dir2")) == "/etc/../dir2"); | |
assert(URIAddress("/etc/dir1/").sibling(URIAddress("dir2/file.ext")) == "/etc/dir2/file.ext"); | |
} | |
@safe /+@nogc+/: | |
/// | |
string URIProtocolPrefix(string value) { | |
bool melformed; | |
scope(exit) | |
assert(!melformed); | |
return URIProtocolPrefix(value, melformed); | |
} | |
/// | |
string URIProtocolPrefix(string value, out bool melformed) { | |
import std.string : indexOf; | |
melformed = false; | |
ptrdiff_t i; | |
if ((i = value.indexOf("://")) > 0) { | |
return value[0 .. i]; | |
} else if ((i = value.indexOf(":\\\\")) > 0) { | |
return value[0 .. i]; | |
} else if ((i = value.indexOf(":/")) > 0) { | |
melformed = true; | |
return null; | |
} else if ((i = value.indexOf(":\\")) > 0) { | |
melformed = true; | |
return null; | |
} else | |
return null; | |
} | |
/// | |
unittest { | |
bool melformed; | |
assert(URIProtocolPrefix("abc", melformed) is null); | |
assert(!melformed); | |
assert(URIProtocolPrefix("ab", melformed) is null); | |
assert(!melformed); | |
assert(URIProtocolPrefix("a/b/c.d", melformed) is null); | |
assert(!melformed); | |
assert(URIProtocolPrefix("d:/", melformed) is null); | |
assert(melformed); | |
assert(URIProtocolPrefix("d://", melformed) == "d"); | |
assert(!melformed); | |
assert(URIProtocolPrefix("d://a/b.c", melformed) == "d"); | |
assert(!melformed); | |
assert(URIProtocolPrefix("D://a/b.c", melformed) == "D"); | |
assert(!melformed); | |
assert(URIProtocolPrefix("a\\b\\c.d", melformed) is null); | |
assert(!melformed); | |
assert(URIProtocolPrefix("d:\\", melformed) is null); | |
assert(melformed); | |
assert(URIProtocolPrefix("d:\\\\", melformed) == "d"); | |
assert(!melformed); | |
assert(URIProtocolPrefix("d:\\\\a\\b.c", melformed) == "d"); | |
assert(!melformed); | |
assert(URIProtocolPrefix("D:\\\\a\\b.c", melformed) == "D"); | |
assert(!melformed); | |
} | |
/// | |
string URIProtocolSuffix(string value) { | |
import std.string : indexOf; | |
if (value.length < 3) | |
return null; | |
ptrdiff_t i; | |
if ((i = value.indexOf("://")) > 0) { | |
if (value.length > i + 3) | |
return value[i + 3 .. $]; | |
else | |
return null; | |
} else if ((i = value.indexOf(":\\\\")) > 0) { | |
if (value.length > i + 3) | |
return value[i + 3 .. $]; | |
else | |
return null; | |
} else | |
return value; | |
} | |
/// | |
unittest { | |
assert(URIProtocolSuffix("a/b/c") == "a/b/c"); | |
assert(URIProtocolSuffix("a\\b\\c") == "a\\b\\c"); | |
assert(URIProtocolSuffix("a://b/c") == "b/c"); | |
assert(URIProtocolSuffix("a:\\\\b\\c") == "b\\c"); | |
} | |
/// | |
string URIAuthentication(string value) { | |
import std.string : indexOf; | |
string input = URIProtocolSuffix(value); | |
ptrdiff_t i; | |
if ((i = input.indexOf("@")) > 0) { | |
return input[0 .. i]; | |
} else { | |
return null; | |
} | |
} | |
/// | |
unittest { | |
assert(URIAuthentication("a/b/c") is null); | |
assert(URIAuthentication("a@b/c") == "a"); | |
assert(URIAuthentication("a:b@c/d") == "a:b"); | |
} | |
/// | |
string URIAuthenticationSuffix(string value) { | |
import std.string : indexOf; | |
string input = URIProtocolSuffix(value); | |
ptrdiff_t i; | |
if ((i = input.indexOf("@")) > 0 && input.length - i > 1) { | |
return input[i + 1 .. $]; | |
} else { | |
return input; | |
} | |
} | |
/// | |
unittest { | |
assert(URIAuthenticationSuffix("a/b/c") == "a/b/c"); | |
assert(URIAuthenticationSuffix("a@b/c") == "b/c"); | |
assert(URIAuthenticationSuffix("a:b@c/d") == "c/d"); | |
} | |
/// | |
string URIConnection(string value) { | |
import std.string : indexOf; | |
string prot = URIProtocolPrefix(value); | |
string auth = URIAuthentication(value); | |
if (prot.length == 1 && auth is null) | |
return null; | |
else if (prot.length > 1) { | |
ptrdiff_t i; | |
string input = URIAuthenticationSuffix(value); | |
if ((i = input.indexOf("/")) > 0) { | |
return input[0 .. i]; | |
} else if ((i = input.indexOf("\\")) > 0) { | |
return input[0 .. i]; | |
} else { | |
return input; | |
} | |
} else { | |
return null; | |
} | |
} | |
/// | |
unittest { | |
assert(URIConnection("") is null); | |
assert(URIConnection("d://abc") is null); | |
assert(URIConnection("d:\\\\abc") is null); | |
assert(URIConnection("/etc/something.txt") is null); | |
assert(URIConnection("smb://abc/d/f") == "abc"); | |
assert(URIConnection("smb:\\\\abc\\d\\f") == "abc"); | |
assert(URIConnection("smb://abc") == "abc"); | |
} | |
/// | |
string URIQuery(string value) { | |
import std.string : indexOf; | |
string input = URIAuthenticationSuffix(value); | |
ptrdiff_t i; | |
if ((i = input.indexOf("?")) > 0 && input.length - i > 1) { | |
return input[i + 1 .. $]; | |
} else { | |
return null; | |
} | |
} | |
unittest { | |
assert(URIQuery("abc/d?b=e&z=y") == "b=e&z=y"); | |
assert(URIQuery("smb://abc/d?b=e&z=y") == "b=e&z=y"); | |
assert(URIQuery("/") is null); | |
assert(URIQuery("/?") is null); | |
} | |
/// | |
string URIEntries(string value) { | |
import std.string : indexOf; | |
string input = URIAuthenticationSuffix(value); | |
string prot = URIProtocolPrefix(value); | |
string query = URIQuery(input); | |
if (query !is null) | |
input = input[0 .. $-(query.length + 1)]; | |
ptrdiff_t i; | |
if (prot.length > 1 && (i = input.indexOf("/")) > 0 && input.length - i > 1) { | |
return input[i + 1 .. $]; | |
} else if (prot.length > 1 && (i = input.indexOf("\\")) > 0 && input.length - i > 1) { | |
return input[i + 1 .. $]; | |
} else if (prot.length > 1) { | |
return null; | |
} else { | |
return input; | |
} | |
} | |
/// | |
unittest { | |
assert(URIEntries("d://abc") == "abc"); | |
assert(URIEntries("d:\\\\abc") == "abc"); | |
assert(URIEntries("/etc/something.txt") == "/etc/something.txt"); | |
assert(URIEntries("smb://abc/d/f") == "d/f"); | |
assert(URIEntries("smb:\\\\abc\\d\\f") == "d\\f"); | |
assert(URIEntries("smb://abc") is null); | |
assert(URIEntries("abc/d?b=e&z=y") == "abc/d"); | |
assert(URIEntries("smb://abc/d?b=e&z=y") == "d"); | |
assert(URIEntries("/") is null); | |
assert(URIEntries("/?") is null); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment