Added examples to std.path

This commit is contained in:
Jack Stouffer 2018-04-19 20:12:07 -04:00
parent 024ebe3e42
commit c1399d34b0
2 changed files with 241 additions and 207 deletions

View file

@ -258,7 +258,6 @@ has_public_example="-etc.c.curl,\
-std.net.isemail,\
-std.numeric,\
-std.parallelism,\
-std.path,\
-std.process,\
-std.range.interfaces,\
-std.regex,\

View file

@ -151,6 +151,21 @@ bool isDirSeparator(dchar c) @safe pure nothrow @nogc
return false;
}
///
@safe pure nothrow @nogc unittest
{
version(Windows)
{
assert( '/'.isDirSeparator);
assert( '\\'.isDirSeparator);
}
else
{
assert( '/'.isDirSeparator);
assert(!'\\'.isDirSeparator);
}
}
/* Determines whether the given character is a drive separator.
@ -333,14 +348,24 @@ enum CaseSensitive : bool
*/
osDefault = osDefaultCaseSensitivity
}
///
@safe unittest
{
assert(baseName!(CaseSensitive.no)("dir/file.EXT", ".ext") == "file");
assert(baseName!(CaseSensitive.yes)("dir/file.EXT", ".ext") != "file");
version(Posix)
assert(relativePath!(CaseSensitive.no)("/FOO/bar", "/foo/baz") == "../bar");
else
assert(relativePath!(CaseSensitive.no)(`c:\FOO\bar`, `c:\foo\baz`) == `..\bar`);
}
version (Windows) private enum osDefaultCaseSensitivity = false;
else version (OSX) private enum osDefaultCaseSensitivity = false;
else version (Posix) private enum osDefaultCaseSensitivity = true;
else static assert(0);
/**
Params:
cs = Whether or not suffix matching is case-sensitive.
@ -356,21 +381,6 @@ else static assert(0);
the comparison is case sensitive or not. See the
$(LREF filenameCmp) documentation for details.
Example:
---
assert(baseName("dir/file.ext") == "file.ext");
assert(baseName("dir/file.ext", ".ext") == "file");
assert(baseName("dir/file.ext", ".xyz") == "file.ext");
assert(baseName("dir/filename", "name") == "file");
assert(baseName("dir/subdir/") == "subdir");
version (Windows)
{
assert(baseName(`d:file.ext`) == "file.ext");
assert(baseName(`d:\dir\file.ext`) == "file.ext");
}
---
Note:
This function $(I only) strips away the specified suffix, which
doesn't necessarily have to represent an extension.
@ -401,26 +411,6 @@ if (isSomeChar!C)
return _baseName(path);
}
private R _baseName(R)(R path)
if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R)
{
auto p1 = stripDrive(path);
if (p1.empty)
{
version (Windows) if (isUNC(path))
return path[0 .. 1];
static if (isSomeString!R)
return null;
else
return p1; // which is empty
}
auto p2 = rtrimDirSeparators(p1);
if (p2.empty) return p1[0 .. 1];
return p2[lastSeparator(p2)+1 .. p2.length];
}
/// ditto
inout(C)[] baseName(CaseSensitive cs = CaseSensitive.osDefault, C, C1)
(inout(C)[] path, in C1[] suffix)
@ -436,6 +426,22 @@ if (isSomeChar!C && isSomeChar!C1)
else return p;
}
///
@safe unittest
{
assert(baseName("dir/file.ext") == "file.ext");
assert(baseName("dir/file.ext", ".ext") == "file");
assert(baseName("dir/file.ext", ".xyz") == "file.ext");
assert(baseName("dir/filename", "name") == "file");
assert(baseName("dir/subdir/") == "subdir");
version (Windows)
{
assert(baseName(`d:file.ext`) == "file.ext");
assert(baseName(`d:\dir\file.ext`) == "file.ext");
}
}
@safe unittest
{
assert(baseName("").empty);
@ -508,6 +514,26 @@ if (isSomeChar!C && isSomeChar!C1)
assert(sa.baseName == "test");
}
private R _baseName(R)(R path)
if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || isNarrowString!R)
{
auto p1 = stripDrive(path);
if (p1.empty)
{
version (Windows) if (isUNC(path))
return path[0 .. 1];
static if (isSomeString!R)
return null;
else
return p1; // which is empty
}
auto p2 = rtrimDirSeparators(p1);
if (p2.empty) return p1[0 .. 1];
return p2[lastSeparator(p2)+1 .. p2.length];
}
/** Returns the parent directory of path. On Windows, this
includes the drive letter if present.
@ -536,52 +562,6 @@ if (isSomeChar!C)
return _dirName(path);
}
private auto _dirName(R)(R path)
{
static auto result(bool dot, typeof(path[0 .. 1]) p)
{
static if (isSomeString!R)
return dot ? "." : p;
else
{
import std.range : choose, only;
return choose(dot, only(cast(ElementEncodingType!R)'.'), p);
}
}
if (path.empty)
return result(true, path[0 .. 0]);
auto p = rtrimDirSeparators(path);
if (p.empty)
return result(false, path[0 .. 1]);
version (Windows)
{
if (isUNC(p) && uncRootLength(p) == p.length)
return result(false, p);
if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2)
return result(false, path[0 .. 3]);
}
auto i = lastSeparator(p);
if (i == -1)
return result(true, p);
if (i == 0)
return result(false, p[0 .. 1]);
version (Windows)
{
// If the directory part is either d: or d:\
// do not chop off the last symbol.
if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1]))
return result(false, p[0 .. i+1]);
}
// Remove any remaining trailing (back)slashes.
return result(false, rtrimDirSeparators(p[0 .. i]));
}
///
@safe unittest
{
@ -673,8 +653,51 @@ private auto _dirName(R)(R path)
//static assert(dirName("dir/file".byChar).array == "dir");
}
private auto _dirName(R)(R path)
{
static auto result(bool dot, typeof(path[0 .. 1]) p)
{
static if (isSomeString!R)
return dot ? "." : p;
else
{
import std.range : choose, only;
return choose(dot, only(cast(ElementEncodingType!R)'.'), p);
}
}
if (path.empty)
return result(true, path[0 .. 0]);
auto p = rtrimDirSeparators(path);
if (p.empty)
return result(false, path[0 .. 1]);
version (Windows)
{
if (isUNC(p) && uncRootLength(p) == p.length)
return result(false, p);
if (p.length == 2 && isDriveSeparator(p[1]) && path.length > 2)
return result(false, path[0 .. 3]);
}
auto i = lastSeparator(p);
if (i == -1)
return result(true, p);
if (i == 0)
return result(false, p[0 .. 1]);
version (Windows)
{
// If the directory part is either d: or d:\
// do not chop off the last symbol.
if (isDriveSeparator(p[i]) || isDriveSeparator(p[i-1]))
return result(false, p[0 .. i+1]);
}
// Remove any remaining trailing (back)slashes.
return result(false, rtrimDirSeparators(p[0 .. i]));
}
/** Returns the root directory of the specified path, or `null` if the
path is not rooted.
@ -698,38 +721,6 @@ if (isSomeChar!C)
return _rootName(path);
}
private auto _rootName(R)(R path)
{
if (path.empty)
goto Lnull;
version (Posix)
{
if (isDirSeparator(path[0])) return path[0 .. 1];
}
else version (Windows)
{
if (isDirSeparator(path[0]))
{
if (isUNC(path)) return path[0 .. uncRootLength(path)];
else return path[0 .. 1];
}
else if (path.length >= 3 && isDriveSeparator(path[1]) &&
isDirSeparator(path[2]))
{
return path[0 .. 3];
}
}
else static assert(0, "unsupported platform");
assert(!isRooted(path));
Lnull:
static if (is(StringTypeOf!R))
return null; // legacy code may rely on null return rather than slice
else
return path[0 .. 0];
}
///
@safe unittest
{
@ -777,6 +768,37 @@ Lnull:
}
}
private auto _rootName(R)(R path)
{
if (path.empty)
goto Lnull;
version (Posix)
{
if (isDirSeparator(path[0])) return path[0 .. 1];
}
else version (Windows)
{
if (isDirSeparator(path[0]))
{
if (isUNC(path)) return path[0 .. uncRootLength(path)];
else return path[0 .. 1];
}
else if (path.length >= 3 && isDriveSeparator(path[1]) &&
isDirSeparator(path[2]))
{
return path[0 .. 3];
}
}
else static assert(0, "unsupported platform");
assert(!isRooted(path));
Lnull:
static if (is(StringTypeOf!R))
return null; // legacy code may rely on null return rather than slice
else
return path[0 .. 0];
}
/**
Get the drive portion of a path.
@ -804,21 +826,6 @@ if (isSomeChar!C)
return _driveName(path);
}
private auto _driveName(R)(R path)
{
version (Windows)
{
if (hasDrive(path))
return path[0 .. 2];
else if (isUNC(path))
return path[0 .. uncRootLength(path)];
}
static if (isSomeString!R)
return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice
else
return path[0 .. 0];
}
///
@safe unittest
{
@ -874,6 +881,20 @@ private auto _driveName(R)(R path)
}
}
private auto _driveName(R)(R path)
{
version (Windows)
{
if (hasDrive(path))
return path[0 .. 2];
else if (isUNC(path))
return path[0 .. uncRootLength(path)];
}
static if (isSomeString!R)
return cast(ElementEncodingType!R[]) null; // legacy code may rely on null return rather than slice
else
return path[0 .. 0];
}
/** Strips the drive from a Windows path. On POSIX, the path is returned
unaltered.
@ -896,16 +917,6 @@ if (isSomeChar!C)
return _stripDrive(path);
}
private auto _stripDrive(R)(R path)
{
version(Windows)
{
if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length];
else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length];
}
return path;
}
///
@safe unittest
{
@ -956,6 +967,16 @@ private auto _stripDrive(R)(R path)
}
}
private auto _stripDrive(R)(R path)
{
version(Windows)
{
if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length];
else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length];
}
return path;
}
/* Helper function that returns the position of the filename/extension
separator dot in path.
@ -1085,12 +1106,6 @@ if (isSomeChar!C)
return _stripExtension(path);
}
private auto _stripExtension(R)(R path)
{
immutable i = extSeparatorPos(path);
return i == -1 ? path : path[0 .. i];
}
///
@safe unittest
{
@ -1127,6 +1142,11 @@ private auto _stripExtension(R)(R path)
assert(stripExtension("file.ext1.ext2"d.byDchar).array == "file.ext1");
}
private auto _stripExtension(R)(R path)
{
immutable i = extSeparatorPos(path);
return i == -1 ? path : path[0 .. i];
}
/** Sets or replaces an extension.
@ -1240,18 +1260,6 @@ if (isSomeChar!C1 && isSomeChar!C2)
return _withExtension(path, ext);
}
private auto _withExtension(R, C)(R path, C[] ext)
{
import std.range : only, chain;
import std.utf : byUTF;
alias CR = Unqual!(ElementEncodingType!R);
auto dot = only(CR('.'));
if (ext.length == 0 || ext[0] == '.')
dot.popFront(); // so dot is an empty range, too
return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR);
}
///
@safe unittest
{
@ -1279,6 +1287,18 @@ private auto _withExtension(R, C)(R path, C[] ext)
assert(equal(sa.withExtension(".txt"), "foo.txt"));
}
private auto _withExtension(R, C)(R path, C[] ext)
{
import std.range : only, chain;
import std.utf : byUTF;
alias CR = Unqual!(ElementEncodingType!R);
auto dot = only(CR('.'));
if (ext.length == 0 || ext[0] == '.')
dot.popFront(); // so dot is an empty range, too
return chain(stripExtension(path).byUTF!CR, dot, ext.byUTF!CR);
}
/** Params:
path = A path name.
ext = The default extension to use.
@ -1344,28 +1364,6 @@ if (isSomeChar!C1 && isSomeChar!C2)
return _withDefaultExtension(path, ext);
}
private auto _withDefaultExtension(R, C)(R path, C[] ext)
{
import std.range : only, chain;
import std.utf : byUTF;
alias CR = Unqual!(ElementEncodingType!R);
auto dot = only(CR('.'));
immutable i = extSeparatorPos(path);
if (i == -1)
{
if (ext.length > 0 && ext[0] == '.')
ext = ext[1 .. $]; // remove any leading . from ext[]
}
else
{
// path already has an extension, so make these empty
ext = ext[0 .. 0];
dot.popFront();
}
return chain(path.byUTF!CR, dot, ext.byUTF!CR);
}
///
@safe unittest
{
@ -1395,6 +1393,28 @@ private auto _withDefaultExtension(R, C)(R path, C[] ext)
assert(equal(sa.withDefaultExtension(".txt"), "foo.txt"));
}
private auto _withDefaultExtension(R, C)(R path, C[] ext)
{
import std.range : only, chain;
import std.utf : byUTF;
alias CR = Unqual!(ElementEncodingType!R);
auto dot = only(CR('.'));
immutable i = extSeparatorPos(path);
if (i == -1)
{
if (ext.length > 0 && ext[0] == '.')
ext = ext[1 .. $]; // remove any leading . from ext[]
}
else
{
// path already has an extension, so make these empty
ext = ext[0 .. 0];
dot.popFront();
}
return chain(path.byUTF!CR, dot, ext.byUTF!CR);
}
/** Combines one or more path segments.
This function takes a set of path segments, given as an input
@ -2532,35 +2552,17 @@ if (isConvertibleToString!R)
/** Determines whether a path starts at a root directory.
Params: path = A path name.
Returns: Whether a path starts at a root directory.
Params:
path = A path name.
Returns:
Whether a path starts at a root directory.
On POSIX, this function returns true if and only if the path starts
with a slash (/).
---
version (Posix)
{
assert(isRooted("/"));
assert(isRooted("/foo"));
assert(!isRooted("foo"));
assert(!isRooted("../foo"));
}
---
On Windows, this function returns true if the path starts at
the root directory of the current drive, of some other drive,
or of a network drive.
---
version (Windows)
{
assert(isRooted(`\`));
assert(isRooted(`\foo`));
assert(isRooted(`d:\foo`));
assert(isRooted(`\\foo\bar`));
assert(!isRooted("foo"));
assert(!isRooted("d:foo"));
}
---
*/
bool isRooted(R)(R path)
if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
@ -2571,6 +2573,27 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
else version (Windows) return isAbsolute!(BaseOf!R)(path);
}
///
@safe unittest
{
version (Posix)
{
assert( isRooted("/"));
assert( isRooted("/foo"));
assert(!isRooted("foo"));
assert(!isRooted("../foo"));
}
version (Windows)
{
assert( isRooted(`\`));
assert( isRooted(`\foo`));
assert( isRooted(`d:\foo`));
assert( isRooted(`\\foo\bar`));
assert(!isRooted("foo"));
assert(!isRooted("d:foo"));
}
}
@safe unittest
{
@ -2596,9 +2619,6 @@ if (isRandomAccessRange!R && isSomeChar!(ElementType!R) ||
assert(!isRooted(DirEntry("foo")));
}
/** Determines whether a path is absolute or not.
Params: path = A path name.
@ -4076,6 +4096,21 @@ string expandTilde(string inputPath) nothrow
}
}
///
@system unittest
{
version (Posix)
{
import std.process : environment;
auto oldHome = environment["HOME"];
scope(exit) environment["HOME"] = oldHome;
environment["HOME"] = "dmd/test";
assert(expandTilde("~/") == "dmd/test/");
assert(expandTilde("~") == "dmd/test");
}
}
@system unittest
{